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: Imaging With QuickDraw /
Chapter 2 - Basic QuickDraw


Using Basic QuickDraw

To create a basic QuickDraw drawing environment, you generally

These tasks are explained in greater detail in the rest of this chapter. After performing these tasks, your application can draw into the current graphics port, as described in the next chapter, "QuickDraw Drawing."

System 7 added new features to basic QuickDraw that were not available in earlier versions of system software. In particular, System 7 added

Before using these capabilities, you should make sure they are available by using the Gestalt function with the gestaltSystemVersion selector. Test the low-order word in the response parameter; if the value is $0700 or greater, then the System 7 features of basic QuickDraw are supported.

You can test whether a computer supports only basic QuickDraw with no
Color QuickDraw support by using the Gestalt function with the selector gestaltQuickDrawVersion. The Gestalt function returns a 4-byte value in its response parameter; the low-order word contains QuickDraw version data. If Gestalt returns the value represented by the constant gestaltOriginalQD, then Color QuickDraw is not supported.

The Gestalt function is described in the chapter "Gestalt Manager" of Inside Macintosh: Operating System Utilities.

Initializing Basic QuickDraw

Call the InitGraf procedure to initialize QuickDraw at the beginning of your program, before initializing any other parts of the Toolbox, as shown in the application-defined procedure DoInit in Listing 2-1. The InitGraf procedure initializes both basic QuickDraw and, on computers that suppport it, Color QuickDraw.

Listing 2-1 Initializing QuickDraw

PROCEDURE DoInit;
BEGIN
 DoSetUpHeap;        {perform Memory Manager initialization here}
 InitGraf(@thePort); {initialize QuickDraw}
 InitFonts;          {initialize Font Manager}
 InitWindows;        {initialize Window Manager & other Toolbox }
                     { managers here}
                     {perform all other initializations here}
 InitCursor;         {set cursor to an arrow instead of a clock}
END; {of DoInit}
When your application starts up, the Finder sets the cursor to a wristwatch; this indicates that a lengthy operation is in progress. See the chapter "Cursor Utilities" in this book for information about changing the cursor when appropriate.

Creating Basic Graphics Ports

All graphics operations are performed in graphics ports. Before a basic graphics port can be used, it must be allocated and initialized with the OpenPort procedure. Normally, you don't call OpenPort yourself. In most cases your application draws into a window you've created with the GetNewWindow or NewWindow function (or, for color windows, GetNewCWindow or NewCWindow), or it draws into an offscreen graphics world created with the NewGWorld function. These Window Manager functions (described in the chapter "Window Manager" in Inside Macintosh: Macintosh Toolbox Essentials) and the NewGWorld function (described in the chapter "Offscreen Graphics Worlds" in this book) call OpenPort to create a basic graphics port. See the description of the OpenPort procedure on page 2-35 for a table of initial values for a basic graphics port.

Listing 2-2 shows a simplified application-defined procedure called DoNew that uses the Window Manager function GetNewWindow to create a basic graphics port for computers that do not support color. The GetNewWindow function returns a window pointer, which is defined to be a pointer to graphics port.

Listing 2-2 Using the Window Manager to create a basic graphics port

PROCEDURE DoNew (VAR window: WindowPtr);
VAR
   windStorage:   Ptr;  {memory for window record}
BEGIN
   window := NIL;
   {allocate memory for window record from previously allocated block}
   windStorage := MyPtrAllocationProc;
   IF windStorage <> NIL THEN {memory allocation succeeded}
   BEGIN
      IF gColorQDAvailable THEN {use Gestalt to determine color availability}
         window := GetNewCWindow(rDocWindow, windStorage, WindowPtr(-1))
      ELSE        {create a basic graphics port for a black-and-white screen}
         window := GetNewWindow(rDocWindow, windStorage, WindowPtr(-1));
   END;
   IF (window <> NIL) and (myData <> NIL) THEN
       SetPort(window);
END;
You can allow GetNewWindow to allocate the memory for your window record and its associated basic graphics port. You can maintain more control over memory use, however, by allocating the memory yourself from a block allocated for such purposes during your own initialization routine, and then passing the pointer to GetNewWindow, as shown in Listing 2-2.

When you call the CloseWindow or DisposeWindow procedure to close or dispose of a window, the Window Manager disposes of the graphics port's regions by calling the ClosePort procedure. If you use the CloseWindow procedure, you also dispose of the window record containing the graphics port by calling the Memory Manager procedure DisposePtr.

For detailed information about managing windows, see the chapter "Window Manager" in Inside Macintosh: Macintosh Toolbox Essentials. For detailed information about managing memory, see Inside Macintosh: Memory.

Setting the Graphics Port

Before drawing into the window, Listing 2-2 calls the SetPort procedure to make the window the current graphics port. If your application draws into more than one graphics port, you can call SetPort to set the graphics port into which you want to draw. At times you may need to preserve the current graphics port. As shown in
Listing 2-3, you can do this by calling the GetPort procedure to save the current graphics port, SetPort to set the graphics port you want to draw in, and then SetPort again when you need to restore the previous graphics port. (The procedures also work with color graphics ports.)

Listing 2-3 Saving and restoring a graphics port

PROCEDURE DrawInPort (thePort: GrafPtr);
VAR
   origPort: GrafPtr;
BEGIN
   GetPort(origPort);      {save the original port}
   SetPort(thePort);       {set a new port}
   DoDrawWindow(thePort);  {draw into the new port}
   SetPort(origPort);      {restore the original port}
END;
In this example, the application calling DrawInPort may need to temporarily turn an inactive window into the current graphics port for updating purposes. After drawing into the inactive window, DrawInPort makes the user's active window the current graphics port again.

Note
When your application runs in Color QuickDraw or uses offscreen graphics worlds, it should use the GetGWorld procedure instead of GetPort, and it should use the SetGWorld procedure instead of SetPort. These procedures save and restore the current graphics port for basic and color graphics ports as well as offscreen graphics worlds. See the chapter "Offscreen Graphics Worlds" in this book for more information.

Switching Between Global and Local Coordinate Systems

Each graphics port has its own local coordinate system. Some Toolbox routines return or expect points that are expressed in the global coordinate system, while others use local coordinates. Sometimes you need to use the GlobalToLocal procedure to convert global coordinates to local coordinates, and sometimes you need the LocalToGlobal procedure for the reverse operation. For example, when the Event Manager function WaitNextEvent reports an event, it gives the cursor location (also called the mouse location) in global coordinates; but when you call the Control Manager function FindControl to find out whether the user clicked a control in one of your windows, you pass the cursor location in local coordinates, as shown in Listing 2-4. (The Event Manager and the Control Manager are described in Inside Macintosh: Macintosh Toolbox Essentials.)

Listing 2-4 Changing global coordinates to local coordinates

PROCEDURE DoControlClick (window: WindowPtr; event: EventRecord);
VAR
mouse:      Point;
control:    ControlHandle;
part:       Integer;
windowType: Integer;
BEGIN
      SetPort(window);
      mouse := event.where;   {save the cursor location}
      GlobalToLocal(mouse);   {convert to local coordinates}
      part := FindControl(mouse, window, control);
      CASE part OF
         inButton:   {mouse-down in OK button}
            DoOKButton(mouse, control);
         inCheckBox: {mouse-down in checkbox}
            DoCheckBox(mouse, control);
         OTHERWISE
         ;
      END;  {of CASE for control part codes}
END; {of DoControlClick}

Scrolling the Pixels in the Port Rectangle

If your application scrolls a document in a window, your application can use the ScrollRect procedure to shift the pixels currently displayed for that document, and then it can use the SetOrigin procedure to adjust the window's local coordinate system for drawing a new portion of the document inside the update region of the window.

Scrolling a document in response to the user's manipulation of a scroll bar requires you to use the Control Manager, the Window Manager, and the File Manager in addition to QuickDraw. The chapter "Control Manager" in Inside Macintosh: Macintosh Toolbox Essentials provides a thorough explanaticn of how to scroll through documents. An overview of the necessary tasks is provided here.

A window record contains a graphics port in its first field, and the Window Manager uses the port rectangle of the graphics port as the content area of the window. This allows you to use the QuickDraw routines ScrollRect and SetOrigin--which normally operate on the port rectangle of a graphics port--to manipulate the content area of the window.

The left side of Figure 2-5 illustrates a case where the user has just opened an existing document, and the application displays the top of the document. In this example, the document consists of 35 lines of monospaced text, and the line height throughout is 10 pixels. Therefore, the document is 350 pixels long. The application stores the document in a document record of its own creation. This document record assigns its own coordinate system to the document. When the user first opens the document, the upper-left point of the graphics port's port rectangle (the window origin) is identical to the upper-left point of the document record's own coordinate system: both have a horizontal coordinate of 0 and a vertical coordinate of 0.

In this example, the content area--that is, the port rectangle--of the window displays 15 lines of text, which amount to 150 pixels.

Imagine that the user drags the scroll box part way down the vertical scroll bar. Because the user wishes to scroll down, the application must move the document up so that more of the bottom of the document shows. Moving a document up in response to a user request to scroll down requires a scrolling distance with a negative value. (Likewise, moving a document down in response to a user request to scroll up requires a scrolling distance with a positive value.)

Using the Control Manager functions FindControl, TrackControl, and GetControlValue, the application in this example determines that it must move the document up by 100 pixels--that is, by a scrolling distance of -100 pixels.

The application uses the QuickDraw procedure ScrollRect to shift the pixels currently displayed in the port rectangle of the window by a distance of -100 pixels. This moves the portion of the document displayed in the window upward by 100 pixels (that is, by 10 lines); 5 lines that were previously displayed at the bottom of the window now appear at the top of the window, and the application adds the rest of the window to an update region for later updating.

Figure 2-5 Moving a document relative to its window


The ScrollRect procedure doesn't change the coordinate system of the graphics port for the window; instead it moves the pixels in a specified rectangle (in this case, the port rectangle) to new coordinates that are still in the graphics port's local coordinate system. For purposes of updating the window, you can think of this as changing the coordinates used by the application's document record, as illustrated in the right side of Figure 2-5.

The ScrollRect procedure takes four parameters: a rectangle to scroll, a horizontal distance to scroll, a vertical distance to scroll, and a region handle. Typically, when specifying the rectangle to scroll, your application passes a value representing the port rectangle (that is, the window's content region) minus the scroll bar regions, as shown in Listing 2-5.

Listing 2-5 Using ScrollRect to scroll the bits displayed in the window

PROCEDURE DoGraphicsScroll (window: WindowPtr;
                            hDistance,vDistance: Integer);
VAR
   myScrollRect: Rect;
   updateRegion: RgnHandle;
BEGIN
   {initially, use the window's portRect as the rectangle to scroll:}
   myScrollRect := window^.portRect;
   {subtract vertical and horizontal scroll bars from rectangle}
   myScrollRect.right := myScrollRect.right - 15;
   myScrollRect.bottom := myScrollRect.bottom - 15;
   updateRegion := NewRgn;    {always initialize the update region}
   ScrollRect(myScrollRect, hDistance, vDistance, updateRegion);
   InvalRgn(updateRegion);
   DisposeRgn(updateRegion);
END; {of DoGraphicsScroll}
The pixels that ScrollRect shifts outside of the rectangle specified by the myScrollRect variable are not drawn on the screen, and the bits they represent are not saved--it is your application's responsibility to keep track of this data.

The ScrollRect procedure shifts the image displayed inside the port rectangle by a distance of hDistance pixels horizontally and vDistance pixels vertically; when the DoGraphicsScroll procedure passes positive values in these parameters, ScrollRect shifts the pixels in myScrollRect to the right and down, respectively. This is appropriate when the user intends to scroll left or up because, when the application finishes updating the window, the user sees more of the left and top of the document, respectively. (Remember: to scroll up or left, move the pixels down or right, both of which are in the positive direction.)

When DoGraphicsScroll passes negative values in these parameters, ScrollRect shifts the pixels in myScrollRect to the left or up. This is appropriate when the user intends to scroll right or down because, when the application finishes updating the window, the user sees more of the right and the bottom of the document. (Remember: to scroll down or right, move the bit image up or left, both of which are in the negative direction.)

In Figure 2-5, the application determines a vertical scrolling distance of -100, which it passes in the vDistance parameter as shown here:

ScrollRect(myScrollRect, 0, -100, updateRegion);
If, however, the user were to move the scroll box back to the beginning of the document at this point, the application would determine that it has a distance of 100 pixels to scroll up, and it would therefore pass a positive value of 100 in the vDistance parameter.

By creating an update region for the window, ScrollRect forces an update event. After using ScrollRect to move the bit image that already exists in the window, the application must use its own window-updating code to draw pixels in the update region of the window. (See the chapter "QuickDraw Drawing" in this book for information about drawing into a window.)

As previously explained, ScrollRect in effect changes the coordinates of the application's document record relative to the local coordinates of the port rectangle. In terms of the graphics port's local coordinate system, the upper-left corner of the document now has a vertical coordinate of -100, as shown on the right side of Figure 2-5 on page 2-19. To facilitate updating the window, the application uses the SetOrigin procedure to change the window origin of the port rectangle so that the application can treat the upper-left corner of the document as again having a local horizontal coordinate of 0 and a local vertical coordinate of 0.

The SetOrigin procedure takes two parameters: the first is a new horizontal coordinate for the upper-left corner of the port rectangle, and the second is a new vertical coordinate for the upper-left corner of the port rectangle.

Any time you are ready to update a window (for example, after scrolling it), you can use the Control Manager function GetControlValue to determine the current setting of the horizontal scroll bar, and you can pass this value to SetOrigin as the new horizontal coordinate for the window origin. Then use GetControlValue to determine the current setting of the vertical scroll bar. Pass this value to SetOrigin as the new vertical coordinate for the window origin. Using SetOrigin in this fashion lets you treat the upper-left corner of the document as always having a horizontal coordinate of 0 and a vertical coordinate of 0 when you update (that is, redraw) the document within a window.

For example, after the user manipulates the vertical scroll bar to move (either up or down) to a location 100 pixels from the top of the document, the application makes the following call:

SetOrigin(0, 100);
Although the scrolling distance in Figure 2-5 is -100, which is relative, the current setting for the scroll bar on the right side of the figure is now at 100.

The left side of Figure 2-6 shows how the application uses the SetOrigin procedure to move the window origin so that the upper-left corner of the document now has a horizontal coordinate of 0 and a vertical coordinate of 0 in the graphics port's local coordinate system. This restores the coordinates that the application originally assigned to the document in its document record and makes it easier for the application to draw in the update region of the window.

Figure 2-6 Updating the contents of a scrolled window


After restoring the document's original coordinates, the application updates the window, as shown on the right side of Figure 2-6. The application draws lines 16 through 24, which it stores in its own document record as beginning at a vertical coordinate of 160 and ending at a vertical coordinate of 250.

To review what has happened up to this point: the user has dragged the scroll box down the vertical scroll bar; the application determines that this amounts to a scroll distance
of -100 pixels; the application passes this distance to ScrollRect, which shifts the document displayed in the window upward by 100 pixels and creates an update region for the rest of the window; the application passes the vertical scroll bar's current setting (100 pixels) in a parameter to SetOrigin so that the upper-left corner of the document has a horizontal coordinate of 0 and a vertical coordinate of 0 in the local coordinate system of the graphics port; and, finally, the application draws the text in the update region of the window.

However, the window origin of the port rectangle cannot be left at the point with a horizontal value of 0 and a vertical value of 100; instead, the application must use SetOrigin to reset it to a horizontal coordinate of 0 and a vertical coordinate of 0 after performing its own drawing, because the Window Manager and Control Manager always assume the window's upper-left point has a horizontal coordinate of 0 and a vertical coordinate of 0 when they draw in a window. Figure 2-7 shows how the application uses SetOrigin to set the upper-left corner of the port rectangle back to a horizontal coordinate of 0 and a vertical coordinate of 0 at the conclusion of its window-updating routine.

Figure 2-7 Restoring the window origin of the port rectangle to a horizontal coordinate of 0 and a vertical coordinate of 0


This example illustrates how to use SetOrigin to offset the port rectangle's coordinate system so that you can treat objects in a document as fixed in the document's own coordinate space. Alternatively, it's possible to leave the coordinate system for the graphics port fixed and instead offset the items in a document by the amount equal to the scroll bar settings. The OffsetRect and OffsetRgn procedures (which are described in the chapter "QuickDraw Drawing"), the SubPt procedure (described on page 2-49), and the AddPt procedure (described on page 2-48) are useful if you pursue this approach. However, it is recommended that you use SetOrigin instead.

IMPORTANT
For optimal performance and future compatibility, you should use the SetOrigin procedure when reconciling document coordinate space with the local coordinate system of your graphics port.
The SetOrigin procedure does not move the window's clipping region. If you use clipping regions in your windows, use the GetClip procedure (described on page 2-44) to store your clipping region immediately after your first call to SetOrigin. Before calling your own window-drawing routine, use the ClipRect procedure (described on page 2-45) to define a new clipping region--to avoid drawing over your scroll bars, for example. (Listing 3-9 on page 3-25 in the chapter "QuickDraw Drawing" illustrates how to do this.) After calling your own window-drawing routine, use the SetClip procedure (described on page 2-44) to restore the original clipping region. You can then call SetOrigin again to restore the window origin to a horizontal coordinate of 0 and a vertical coordinate of 0 with your original clipping region intact.


Previous Book Contents Book Index Next

© Apple Computer, Inc.
7 JUL 1996