Legacy Documentclose button

Important: The information in this document is obsolete and should not be used for new development.

Previous Book Contents Book Index Next

Inside Macintosh: Programmer's Guide to MacApp / Part 2 - Working With MacApp
Chapter 21 - Working With the Cursor


Recipes--The Cursor

The recipes and sample code in this section demonstrate how to work with and modify MacApp's basic cursor handling.

Recipe--Defining a Cursor Resource

Some of the standard cursors shown in Figure 21-1 are available as system resources, specified by the cursor ID constants iBeamCursor, crossCursor, plusCursor, and watchCursor. The arrow cursor can be specified using the global QDGlobals structure qd, with code like the following:

SetCursor(&(qd.arrow));
MacApp supplies resource definitions for additional cursor resources:

BusyCursor.r
This file defines cursor resources for an animated watch cursor.
MacApp.r
This file defines the open-hand and closed-hand cursor resources used for drag-and-drop operations (specified by the constants kOpenHandCursor and kClosedHandCursor).
To make other cursor images available to your application, you define the cursor in a resource file, along with a constant to specify it. Cursors are defined as 'CURS' resources, which contain hexadecimal data and have the following general appearance when displayed in text format:

#define kPencilCursorId 1000

resource 'CURS' (kPencilCursorId, purgeable) 
{
   $"00F0 0088 0108 0190 0270 0220 0440 0440"
   "0880 0880 1100 1E00 1C00 1800 10",
   $"00F0 00F8 01F8 01F0 03F0 03E0 07C0"
   "07C0 0F80 0F80 1100 1200 1400 1800 10",
   {14, 3}
};
To access the pencil cursor in your application, you define a constant to match the one in the resource file:

const short kPencilCursorId = 1000;
You can then use the constant kPencilCursorId to specify the pencil cursor anywhere in your application where a cursor ID is required.

Note
You can create cursor resources graphically, using an application such as ResEdit. You can then use the MPW tool DeRez to convert between compiled resources (the output of ResEdit) and text resource definitions (such as the definition of the pencil cursor shown above). The Rez tool lets you convert in the opposite direction.

Recipe--Associating a Cursor Image With a View

When a user moves the cursor over a view that handles the cursor (its fHandlesCursor field is TRUE), MacApp automatically sets the cursor image according to the view's fCursorID field. The cursor region is assumed to be the entire view.

You can associate a cursor image with a view, either when you create your application or while your application is running, by setting the view's fCursorID field to the resource ID of a 'CURS' resource. The resource may be stored in your application, or it may be one of the available system cursor resources.

You set the fCursorID field when you create your application by using one of the following two mechanisms:

To associate a cursor image with a view while your application is running, you set the view's fCursorID field directly in your code.

IMPORTANT
MacApp's default cursor handling expects the fCursorID field to refer to a 'CURS' resource, not a color 'crsr' resource. For more information, see "Recipe--Displaying a Color Cursor" on page 495.

Recipe--Setting the Cursor Image Over a View

To set the cursor image over a view in your application, you perform these steps:

  1. Define a cursor resource of type 'CURS', or use a resource defined by MacApp or by the Macintosh Operating System.
  2. Define a cursor ID constant for your cursor resource, or use a constant defined by MacApp or by the operating system.
  3. Override the DoSetCursor method in your view class. Your version should

    • call the Toolbox routine GetCursor to get a handle to the desired cursor
    • call the Toolbox routine SetCursor to set the cursor
    • set the cursor region if is different from the one passed to DoSetCursor

Steps 1 and 2 are shown in "Recipe--Defining a Cursor Resource" on page 489.

The sample code shown in this recipe is from the IconEdit application.

Overriding the DoSetCursor Method in Your View Class

The following is the DoSetCursor method of the TIconEditView class. This method computes an inner region that excludes the view's border area. If the cursor is in that region, DoSetCursor sets the cursor to the pencil cursor and sets the cursor region to the inner region. If the cursor is not in the inner region, this method sets the cursor image to the arrow cursor and sets the cursor region to the region for the border area.

void TIconEditView::DoSetCursor(const VPoint&localPoint,
                            RgnHandlecursorRegion) // New region.
{
   VPoint   iconBit;
   CursHandlepencilCursor;

   CTemporaryRegion outerRegion;
   CTemporaryRegion innerRegion;
   
   // Compute a region consisting of the view's extent, minus
   // the border region.
   this->GetExtentRegion(outerRegion);
   CopyRgn(outerRegion,innerRegion);
   InsetRgn(innerRegion,kBorder,kBorder);

   // PointToBit ensures the cursor is in the inner region.
   if (this->PointToBit(localPoint, iconBit))
   {
      // Get the pencil cursor resource and set the cursor.
      pencilCursor = GetCursor(this->GetCursorID());
      FailNILResource((Handle)pencilCursor);
      SetCursor(*pencilCursor);
      // Set the cursor region to the one we computed.
      CopyRgn(innerRegion,cursorRegion);
   }
   else
   {
      // Cursor in border region, so set image to arrow,
      // and set cursor region to border region.
      SetCursor(&(qd.arrow)); 
      DiffRgn(outerRegion,innerRegion,cursorRegion);
   }
} // TIconEditView::DoSetCursor 
The reference to qd.arrow specifies the arrow cursor from the global QDGlobals structure qd, a part of the operating system.

Recipe--Setting the Cursor Image Over a Shape Within
a View

You may want to set the cursor to a specific image when it is over an item within a view. For example, a shape-drawing application might want to set a particular cursor whenever a user moves the cursor over a selected shape. This recipe is based on just such a shape-drawing application, which defines a TShapeDocument class to store shapes and a TShapeView class to display them.

To set the cursor image for a shape, you perform these steps:

  1. Define a method that determines whether the current cursor location is over a shape in the shape view.

    • Because the shape document class stores the shapes displayed in the view, the method should belong to the document.
    • If the cursor is over a shape, the method sets the cursor and returns the shape's region for use as the cursor region.

  2. Override the DoSetCursor method in the view class that displays the shapes.

    • Call the method defined in step 1 to determine whether the cursor is over a shape in the view.
    • If it is, set the cursor region to the region of the shape.

The sample code shown in this recipe is for a hypothetical application.

Defining a Method to Determine if the Cursor Is Over a Shape

The TShapeDocument class defines the CursorInShape method to determine whether the cursor is over a shape.

Boolean TShapeDocument::CursorInShape(CPoint qdPoint,
                              RgnHandlecursorRegion);
The CursorInShape method is called by the shape view's DoSetCursor method, shown in the next section. DoSetCursor passes the current cursor location and a RgnHandle for setting the cursor region.

CursorInShape iterates over the document's shapes and determines whether the passed location is over a shape. If so, it sets the cursor image to the plus cursor and sets the passed RgnHandle to the shape's region. It returns TRUE if it sets the cursor and FALSE if it does not.

Overriding the DoSetCursor Method in the View Class

The TShapeView class defines a DoSetCursor method as follows:

void TShapeView::DoSetCursor(const VPoint& localPoint, 
                        RgnHandle cursorRegion)
{
   CPoint qdPoint = ViewToQDPt(localPoint);

   // If the cursor is over a shape, CursorInShape sets it to 
   // the plus cursor and sets the region to the shape's region.
   Boolean cursorSet = 
         fShapeDocument->CursorInShape(qdPoint, cursorRegion);
   // If the cursor wasn't over a shape, set it to either
   // a color cursor or the black-and-white arrow cursor.
   if (!cursorSet)
   {
      if (qNeedsColorQD || HasColorQD())
         SetCCursor(gRainbowArrow);// Set cursor to color arrow.
      else
         SetCursor(&qd.arrow);
   }
}
The DoSetCursor method calls CursorInShape to set the cursor if it is over a shape. If the cursor isn't over a shape, DoSetCursor sets it to a colored arrow cursor, if possible. Otherwise, it sets the cursor to the standard arrow cursor. The use of a color cursor is described in the next recipe.

Recipe--Displaying a Color Cursor

To display a color cursor (resource type 'crsr'), you use the Toolbox routine GetCCursor to get the cursor and the routine SetCCursor to set it. GetCCursor is unlike GetCursor, in that it makes a copy of the cursor data. As a result, you can read a color cursor once with GetCCursor, then set it many times with SetCCursor without having to allocate storage each time.

The following definition specifies a color arrow cursor, or "rainbow" cursor, in a resource definition file:

#define kRainbowArrow 140

// The color "rainbow" arrow cursor.
data 'crsr' (kRainbowArrow, "Rainbow Arrow") {
   $"8001 0000 0062 0000 0096 0000 0000 0000"
   $"0000 0000 0000 4000 6000 7000 7800 7C00"
   $"7E00 7F00 7F80 7C00 6C00 4600 0600 0300"
   $"0100 0000 C000 E000 F000 F800 FC00 FE00"
   $"FF00 FF80 FFC0 FFE0 FE00 EF00 CF00 8780"
   $"0780 0380 0001 0001 0000 0000 0000 0000"
   $"0000 0000 0000 8008 0000 0000 0010 0010"
   $"0000 0000 0000 0000 0048 0000 0048 0000"
   $"0000 0004 0000 0000 0000 0000 0000 0116"
   $"0000 0000 0000 0000 0000 0000 0000 0100"
   $"0000 0000 0000 0110 0000 0000 0000 0121"
   $"0000 0000 0000 0222 2000 0000 0000 0233"
   $"3200 0000 0000 0333 3330 0000 0000 0344"
   $"4445 0000 0000 0444 4445 5000 0000 0444"
   $"5500 0000 0000 0550 6600 0000 0000 0500"
   $"0660 0000 0000 0000 0660 0000 0000 0000"
   $"0066 0000 0000 0000 0066 0000 0000 0000"
   $"0000 0000 0000 0000 0000 0000 0006 0000"
   $"FFFF FFFF FFFF 0001 0000 DB00 0000 0002"
   $"FFFF DB00 0000 0003 FFFF 4F00 0000 0004"
   $"DB00 0000 0000 0005 9200 0000 B000 0006"
   $"0000 0000 FFFF"
};
To make the color cursor available to your application, you can define a global cursor variable:

CCrsrHandle gRainbowArrow;
To initialize the global reference to the cursor, the initialization method for your application class uses code similar to the following:

gRainbowArrow = GetCCursor(kRainbowArrow);
FailNIL(gRainbowArrow);
The DoSetCursor method shown in the previous recipe includes the following code to display the color cursor:

if (qNeedsColorQD || HasColorQD())
   SetCCursor(gRainbowArrow);// Set cursor to color arrow.
else
   SetCursor(&qd.arrow);

Recipe--Displaying Cursor Regions in a Debug Application

You can use MacApp's debugging code to display various cursor regions, including the cursor region, sleep region, help region, and invalid regions. To do so, you follow these steps:

  1. Build a debug version of your application. Appendix A describes how to build a version of your application that includes MacApp's debugging code.
  2. Run the application.
  3. Choose the Show Debug Flags Window menu command from the Debug menu.
  4. Check any of the four "Show regions" checkboxes:

    • Cursor region
    • Sleep region
    • Help region
    • Invalid regions

As you move the cursor over your application and the Macintosh desktop, the regions you have checked will flash between a normal display and an inverted display, based on the current cursor position. This can be quite helpful in determining whether cursor regions are computed correctly, though it can definitely put a strain on your eyes.

Recipe--Modifying Busy Cursor Behavior

MacApp has built-in machinery to display an animated watch cursor when your application is busy for an extended period of time. MacApp's busy cursor mechanism is implemented by the TBusyCursor class. During initialization, MacApp creates an instance of TBusyCursor and stores a reference to it in gBusyCursor.

The TBusyCursor class has the following default behavior:

Some of the ways you can modify busy cursor behavior include the following:


Previous Book Contents Book Index Next

© Apple Computer, Inc.
25 JUL 1996