TweetFollow Us on Twitter

September 93 - GETTING STARTED WITH QUICKDRAW GX

GETTING STARTED WITH QUICKDRAW GX

PETE ("LUKE") ALEXANDER

[IMAGE 041-042_Graphical_Truffl1.GIF]

A beta version of QuickDraw GX comes to you on this issue's CD. As you contemplate the vast scope of it all, you may wonder how you're ever going to get your arms around this new imaging technology. Not to worry -- this article will get you started. It walks you through the steps to getting QuickDraw GX up and drawing and presents a simple "GX-hip" application shell that incorporates the basics for you to experiment with.


QuickDraw GX offers developers a totally new and markedly improved way of imaging on the Macintosh. Yes, you'll have to learn the new system, but look at what you get: The API is simpler and the human interface is better. The amount of control your application can exercise over text and graphics has been greatly increased. Your application will be able to produce consistent output no matter what the output device. And extensive support for color is built in throughout the system.

With the beta version of QuickDraw GX in hand, you're no doubt eager to create a QuickDraw GX application and start drawing. This article covers just about everything you need to know to get started: initializing QuickDraw GX, using windows, creating and manipulating QuickDraw GX shapes, printing, and debugging. By way of illustration, we discuss the QuickDraw GX shell that you'll find on this issue's CD. But before we do that, let's take a quick look under the hood at the major features of QuickDraw GX and how it fits into the Macintosh architecture.

QUICKDRAW GX: A QUICK LOOK UNDER THE HOOD

QuickDraw GX coexists happily with QuickDraw, thank goodness. It doesn't replace QuickDraw, but instead "moves in next door," so you still live with a Macintosh Toolbox based on QuickDraw. You can run QuickDraw-based applications on a QuickDraw GX system. These applications won't even notice that QuickDraw GX is installed, but they'll be able to take advantage of some of the QuickDraw GX printing features, including improved background printing to all devices, desktop printers, print job queuing, and better type management.

QuickDraw GX has three major pieces: graphics, typography, and printing. You can visualize the relationship of these three different pieces to each other and to QuickDraw as shown in Figure 1.

[IMAGE 041-042_Graphical_Truffl2.GIF]

Figure 1 QuickDraw and the Pieces of QuickDraw GX

GRAPHICSThe basic building block of QuickDraw GX graphics is theshape. A shape is an object that contains, among other things, a geometry of some type and a fill property that specifies how the geometry should be interpreted when drawn (such as framed or filled).

There are four basic types of shapes, classified by the nature of the geometry they contain: geometric shapes, typographic shapes, bitmap shapes, and pictures.

  • A geometric shape contains a primitive geometry: a point, a line, a curve, a rectangle, a polygon (a series of points connected by straight lines), or a path (a series of points connected by straight or curved lines). In addition, there are two other special geometric shape types: empty (no geometry at all) and full (covers the entire coordinate system).
  • A typographic shape contains text, glyphs (renditions of individual characters or character combinations over which your application has direct control), or layouts (pieces of text for which QuickDraw GX automatically chooses and positions glyphs, given certain information by the application).
  • A bitmap shape contains a reference to a block of memory containing a bit image, as well as information on how to interpret the bits: the pixel size, color space, color set, and color profile.
  • A picture contains a list of other shapes. The shapes in the list can be other pictures, so that a picture is actually a hierarchical database of shapes.

Besides containing a geometry, a shape contains references to three other objects that describe how it should be rendered. These objects are the style, the transform, and the ink.

  • The styledefines the pen thickness, the place where the pen draws (inside, outside, or on the geometry), the kind of start and end cap (such as round, pointy, or square), and ways to dash, join, and pattern shapes. For a text shape, the style also defines the font, size, variation, and text face.
  • The transformcontrols the skew, scale, perspective, and clipping of the geometry. It also specifies where to draw it and how to hit-test it.
  • The inktells the system which color to draw the geometry in. Ink also includes information about the color matching and transfer mode.

Some of these objects in turn contain references to other objects. For example, a transform points to a list of view port objects that describe where to draw the geometry. A view port is like a QuickDraw grafPort in that it defines an area of local space as a drawing environment. Unlike a grafPort, though, a view port doesn't contain state information about the drawing environment (pen, color, transfermode, and so on). A view port contains the mapping used to convert from the view port's local space to a global space described by aview group. A view port object points in turn to a list of view device objects, which describe the clip shape, mapping, and bitmap associated with a physical device such as a monitor.

A shape can also have one or moreattributes, which modify the shape's behavior. These attributes enable your application to specify how a shape is edited or how QuickDraw GX stores the shape. For example, if you set the shape attribute gxMapTransformShape, this tells QuickDraw GX that you want it to manipulate the transform referenced by the shape, instead of directly manipulating the data contained within the geometry of the shape.

Figure 2 depicts the shape object and what it references. The owner count is the number of other objects within the application that reference that object. The tag list is a list of tag objects, which are simply containers for any data the application associates with the owning object.

[IMAGE 041-042_Graphical_Truffl3.GIF]

Figure 2 The Shape Object and What It References

TYPOGRAPHY
QuickDraw GX has a sophisticated typographic model that's fully integrated with graphics. The ability to do kerning, tracking, and justification, as well as ligatures and ornamental forms of various characters, is provided by the line layout routines, supported by the QuickDraw GX smart font format. The line layout routines work with the typographic information contained in the TrueType GX and Type 1 GX fonts to give you a ton of control over how text is placed on a page.

Because QuickDraw GX typography is fully integrated with graphics, you can rotate, skew, and change the perspective of typographic shapes the same way you can geometric shapes. You can use the text shape to draw a line of text with one style. The glyph shape enables you to draw text in several styles and graphically manipulate each glyph. The layout shape uses the information contained in a TrueType GX or Type 1 GX font to automatically kern, justify, and track, and to support ligatures, final forms (special forms found at the ends of words), and ornamental forms of the various glyphs contained within the layout shape.

Note that although QuickDraw GX supports all existing Macintosh font formats (Type 1, bitmap, and TrueType), to take full advantage of its extensive line layout capabilities you must use TrueType GX or Type 1 GX fonts.

PRINTING
QuickDraw GX improves printing for both users and developers. Users get an improved human interface, and developers get much more control and functionality. From the application's point of view, QuickDraw GX offers true device independence: you can send the same data to all supported devices and the output will be rendered appropriately on each device.

QuickDraw GX introduces three new printing concepts: desktop printers, portable digital documents, and printing extensions.

Users can createdesktop printerswith the Chooser. These are represented as icons on the desktop and are full Finder citizens; users can drag and drop print files and documents to them. Users can also manage the print queue and redirect print files and documents by dragging them to and from desktop printers, and can share desktop printers with other users via PrinterShare GX.

A portable digital document(PDD) file contains all the objects required to render a document on a screen or printer, so you can open, review, and print the file on any system running QuickDraw GX without the application or fonts used to create the document. When a PDD file is created, only the glyphs used in the document are saved along with it; since the document can't very well be edited, the PDD is secure for transporting fonts. When you print, you can save the print job as a PDD with or without the fonts required. If you know that the person you're sending the PDD file to has the fonts you used, you can choose not to save the fonts with the PDD.

Printing extensionsare small standalone pieces of code that modify the behavior of printing and give users vastly increased control, at a system rather than a program level, over how a printed page looks. For example, through a printing extension the user can direct a printer to print "Confidential" diagonally across each page, no matter what program is doing the printing. The user selects a printing extension from a list displayed in the expanded Print, By Page Setup, and Document Setup dialogs (which appear when More Choices is clicked in the regular dialogs).

The API for QuickDraw GX printing gives you easy access to information about the page size and orientation of a print job and enables you to keep the user from changing these settings.

QuickDraw GX supports raster, vector, and PostScriptTM devices. The bad news is that if your system is running QuickDraw GX, you won't be able to use any non-QuickDraw GX printer drivers. The good news is that because QuickDraw GX provides system-level support for developing printer drivers, it's a whole lot easier to develop printer drivers for QuickDraw GX than it is for the old QuickDraw-based printing architecture -- you can plan on months of development time as opposed to years. And in many cases you may find that a printing extension, which is easier yet to develop, will suffice to implement the desired functionality; for more information, see the article "Developing QuickDraw GX Printing Extensions" later in this issue.

PROGRAMMING AMENITIES

QuickDraw GX offers you some truly useful programming goodies: libraries of handy high-level routines, extensive error-handling capabilities, and a powerful new debugging tool called GraphicsBug.

THE QUICKDRAW GX LIBRARIES
As you cruise around the QuickDraw GX folder on this issue's CD, you'll notice a folder named Libraries. Open it and you'll find libraries of code for many common graphics, line layout, and printing tasks. These provide sample code that most applications will need in order to create a QuickDraw GX application. But unlike Macintosh Toolbox code, this library code can be modified or extended by you to meet your own particular needs. All the library code is based on core QuickDraw GX calls.

ERROR HANDLING IN QUICKDRAW GX
The goal of QuickDraw GX's error-handling capabilities is to never allow QuickDraw GX to crash your Macintosh, and to inform you anytime QuickDraw GX can't complete an operation. QuickDraw GX uses two different models for handling errors: one for graphics and layout errors and another for printing errors. We'll discuss graphics and layout errors here. Printing errors are described later in this article, under "Basic Printing in QuickDraw GX."

There's both a debugging and a nondebugging version of the combined graphics and layout portions of QuickDraw GX. The debugging version provides extensive error-handling capabilities to help you debug your applications under development. The nondebugging version is lean and mean; it has fewer error-handling capabilities and is faster than the debugging version. You can differentiate between the two versions by their sizes and version strings: the nondebugging version is smaller, and the version string for the debugging version has the word "debug" in it. When you're developing your QuickDraw GX application, you should be using the debugging version.

In the debugging version, information about internal data and drawing problems comes in three flavors: notices, warnings, and errors. Only a few selected errors and warnings are issued in the nondebugging version.

Notices. A notice informs you that the operation you're performing isn't really needed. Notices aren't necessarily bad things; they're just information to help you improve the efficiency of your application. For example, if you've already colored a shape and you try to color it again, you'll receive the following notice in the installed debugger:

GRAPHICS NOTICE: color already set

Warnings. A warning informs you that QuickDraw GX doesn't allow the operation you're trying to perform. While this might not cause any problems, you also might not get the result you expected. For example, if you try to use a font that isn't available, QuickDraw GX will substitute the default font and give you the following warning:

GRAPHICS WARNING: font substitution took place

Errors. An error means that QuickDraw GX couldn't draw your shape or complete a routine. For example, if you try to draw an empty shape or one that hasn't been defined, you'll receive the following error:

GRAPHICS ERROR: shape is nil

Checking for drawing errors. Once you've finished developing your application, you'll still want to be able to check for drawing errors. The QuickDraw GX routine GXGetShapeDrawError lets you do this and, in case of an error, fail in a graceful manner. For example, this code fragment checks that drawing was successful and fails if it wasn't:

GXDrawShape(gthePage);
if (drawingError = GXGetShapeDrawError(gthePage) != noDrawError)
    // Your error-handling code here!

Ignoring notices and warnings. Sometimes you might want to ignore a particular notice or warning because you know what you're doing. Use these routines to ignore a notice or warning:

void GXIgnoreGraphicsNotice(gxGraphicsNotice notice);
void GXIgnoreGraphicsWarning(gxGraphicsWarning warning);

For example, if you wanted to ignore a notice about recoloring a shape, you would make this call:

void GXIgnoreGraphicsNotice(color_already_set);

When you call GXIgnoreGraphicsNotice or GXIgnoreGraphicsWarning, the notice or warning is added to the top of the notice stack or warning stack, respectively. (It's added to the stack even when not ignored, but the stack handling is taken care of behind the scenes for you in that case.) So you must balance this with a call to one of the following routines to ensure that you don't overflow the notice or warning stack:

void GXPopGraphicsNotice(void);
void GXPopGraphicsWarning(void);

In the nondebugging version, where notices and most warnings aren't available, calling the GXIgnoreGraphicsXXX and GXPopGraphicsXXX routines still results in a trap call and dispatch even though they just return immediately. There may be a small performance penalty for this, so you should remember to remove the unnecessary calls for a shipping application.

Grabbing errors, notices, and warnings. In the nondebugging version, you receive only a few selected errors and warnings. If you've tested your application thoroughly, these should be the only errors you see:

out_of_memory
not_enough_memory_for_graphics_client_heap
graphics_client_memory_too_small
could_not_create_backing_store

These should be the only warnings you see:

<<font or character>>_substitution_occurred
<<map, move, scale, rotate, or skew>>_shape_out_of_range
<<map, move, scale, rotate, or skew>>_transform_out_of_range

You probably don't want your user to end up in a debugger or with a system bomb, so you should catch errors by calling the GXGetGraphicsError routine; you can then handle the error appropriately within your application or present it to the user.

gxGraphicsError GXGetGraphicsError(gxGraphicsError *stickyError);

You can also grab notices (in the debugging version only) and warnings with these calls:

gxGraphicsNotice GXGetGraphicsNotice(gxGraphicsNotice *stickyNotice);
gxGraphicsWarning GXGetGraphicsWarning(gxGraphicsWarning *stickyWarning);

GRAPHICSBUG: A POWERFUL NEW DEBUGGING TOOL
The only way to create and modify shapes in QuickDraw GX is through the public API; you can't operate on any data directly. This is a very good thing because it lets Apple expand the system in the future with minimal compatibility risk. But if you can't see the data you're working with, won't debugging be a nightmare? Here's where GraphicsBug comes to the rescue. GraphicsBug is an application that enables you to inspect the contents of any QuickDraw GX graphics or layout object to make sure it contains the correct information. The command set is very similar to that of MacsBug; just type "?" to get a list of the commands available. GraphicsBug works only in the debugging version of QuickDraw GX 1.0b1 but in both versions of later QuickDraw GX releases.

INITIALIZING QUICKDRAW GX

Now that we've checked out the horsepower under the hood and the amenities built in for programmers, we're ready to get QuickDraw GX up and drawing. The first step is to initialize QuickDraw GX, but before you do, you need to make sure the user has installed it. Use the Gestalt selector 'grfx' to determine whether the graphics and typography portions of QuickDraw GX have been installed, and the Gestalt selector 'pmgr' to determine whether QuickDraw GX printing has been installed.

In the case of our QuickDraw GX shell, the following routine finds out which parts of QuickDraw GX have been installed:

Boolean QuickDrawGXAvailable()
{
    long theFeatureInQuestion;
    
    if (Gestalt('grfx', &theFeatureInQuestion) == noErr)
        {
        if (Gestalt('pmgr', &theFeatureInQuestion) == noErr)
            gQDGXPrintingInstalled = true;
        return (true);
        }
    return (false);
}

The QuickDraw GX shell uses the global variable gQDGXPrintingInstalled to determine if QuickDraw GX printing has been installed. If it has, the printing menu items in the File menu are enabled. Otherwise, an alert tells the user that QuickDraw GX printing hasn't been installed, and the application works without printing.

Once you know that the user has QuickDraw GX, you're ready to initialize it. After the generic Macintosh Toolbox initialization, you create a new graphics client to allocate memory. Then you can set up error handling and validation as an aid to tracking down problems, although if you're eager to get on with drawing, you don't have to do this right now. Finally, you can initialize the common color library to get quick and easy color. In the QuickDraw GX shell, the routine QuickDrawGXInit does all of this initialization.

CREATING A NEW GRAPHICS CLIENT
A graphics client is a reference to the block of MultiFinder memory used by QuickDraw GX graphics and layout called theQuickDraw GX heap. When your application creates a new graphics client, QuickDraw GX usually allocates this block of memory. The QuickDraw GX heap contains all the graphics and layout objects your application creates while running QuickDraw GX, as well as a few objects QuickDraw GX uses to manage the heap. (See "Managing Memory in the QuickDraw GX Heap" for more details.)

The simplest way to create a graphics client is to call EnterGraphics, which defines a client for you based on some fundamental assumptions. If you want to have more control over the graphics client you create, call GXNewGraphicsClient:

gGraphicsClient = GXNewGraphicsClient(nil,
    gGraphicsHeapSize * 1024, 0L);

The variable gGraphicsClient holds the new graphics client. You can use this variable anytime you need to access this graphics client. In our shell, we only need this variable when the application shuts down. The first parameter tells the Memory Manager where we want to create the QuickDraw GXheap. A nil value tells the Memory Manager to create the heap within MultiFinder memory. That's usually where you want it, but you can also specify a pointer to a block of memory in your application heap or even the system heap. The second parameter defines the size of the heap in bytes. Our shell uses a 115K heap (gGraphicsHeapSize = 115); if you pass 0, you get the default heap size of 600K. The last parameter, named separateStack, tells QuickDraw GX to allocate a stack for the graphics client, which is necessary if that client is going to run at interrupt time. To get a separate stack, just pass any nonzero value that defines the stack size.

SETTING UP ERROR HANDLING AND VALIDATION
After setting up the graphics client, you should enable the error, warning, and notice capabilities if you want to make debugging easier for yourself down the line. In our QuickDraw GX shell we enable all three with the routines SetGraphicsLibraryErrors (which enables errors and warnings) and SetGraphicsLibraryNotices (which enables notices).

The debugging version of QuickDraw GX graphics and layout provides extensive validation facilities that let you determine whether you're passing valid data to the QuickDraw GX API and whether anything's gone awry internally. (See "Tracking Down a Memory Trashing Problem" for details on how to use validation to find out how damage is being caused in the QuickDraw GX heap.) In general, you should always run with gxPublicValidation + gxTypeValidation while you're developing a QuickDraw GX application. To set this level of validation, make this call:

GXSetValidation(gxPublicValidation + gxTypeValidation);

This setting ensures that for all calls, QuickDraw GX checks the routines on entry and makes sure that the types are correct. For example, if you call GXDrawShape with this validation setting, QuickDraw GX makes sure that the shape being passed in is a valid shape and that it's an object of the correct type. This will result in a slight decrease in performance but will help you catch bad data earlier.

Your application can validate various QuickDraw GX graphics and layout objects -- shape, style, transform, ink, view port, view device, view group, color profile, color set, tag, or graphics client, or any combination -- with a GXValidate call before they're passed to a routine. For example, if you wanted to validate the inks in the heap, you would make the following call:

GXValidateInk(gxInk);

If you're interested in using only these validation calls, set the validation level to gxPublicValidation.

SETTING UP THE COMMON COLOR LIBRARY
The common color library provided by QuickDraw GX gives you a quick way to color the shapes you create. This library contains 103 common colors, including maroon, teal, fluorescent blue, apple green, Mars orange, and Venetian red. To initialize this library, make this call:

InitCommonColors();

With the color library set up, you can call SetShapeCommonColor to color a shape. We'll do this later with the shapes we draw in the QuickDraw GX shell window.

USING WINDOWS WITH QUICKDRAW GX

Now that you've initialized QuickDraw GX, you need to set it up to work within a window. The first step is to create a view port and attach it to a window. Then you need to provide routines to zoom and resize the view port and to scroll its contents.

Recall that QuickDraw GX draws all shapes in a view port, which contains the mapping used to convert from the view port's local space to the global space (described by a view group). The view group determines whether the contents of the view port are drawn on the screen in a window or to an off-screen area. A view port also contains its clip shape, describing the area in which drawing can take place.

ATTACHING A VIEW PORT TO A WINDOW
By attaching a view port to a window, you guarantee that all the shapes you draw to the view port will be drawn in the correct location even after the user moves the window. You attach a view port to your window with this call:

gxViewPort windowParentViewPort;

windowParentViewPort = GXNewWindowViewPort(theWindow);

If all goes well, windowParentViewPort contains a reference to the view port attached to the window (the parent view port). You can't change the clip shape or mapping of this particular view port, because QuickDraw GX automatically maintains all the characteristics of the parent view port. This is a problem if you don't want your scroll bars to be overwritten. However, you can attach a view port to the parent view port (see Figure 3) and then adjust this newest view port (the child view port) to reflect changes due to scrolling or resizing the window.

[IMAGE 041-042_Graphical_Truffl4.GIF]

Figure 3 The View Port Hierarchy

Defining the new view port's bounds. Before we create a new view port we need to determine its bounds. We can find the bounds of the window by converting the window's QuickDraw portRect into a fixed-point QuickDraw GX rectangle. The following utility routine does this.

void GetFixedWindowBounds(WindowPtr myWindow,
    gxRectangle *boundingBoxPtr)
{
    GrafPtr     oldPort;
    Rect        qdBounds;
    gxRectangle gxBounds;
    
    GetPort(&oldPort);
    SetPort(myWindow);
    qdBounds = myWindow->portRect;
    
    // Convert the QuickDraw rectangle into a GX fixed-point
    // rectangle.
    GXQDGlobalToFixedLocal((Point *) &qdBounds.top, 
                    (gxPoint *) &gxBounds.left);
    GXQDGlobalToFixedLocal((Point *) &qdBounds.bottom, 
                    (gxPoint *) &gxBounds.right);
                    
    *boundingBoxPtr = gxBounds;
    SetPort(oldPort);
}

Now we can create a QuickDraw GX rectangle that represents the portRect of the window. The rectangle will reside in the variable viewRect.

GetFixedWindowBounds(theWindow, &viewRect);

We need to adjust viewRect for the scroll bars attached to the window.

viewRect.right -= ff(kScrollBarWidth - 1);
viewRect.bottom -= ff(kScrollBarWidth - 1);

Creating and activating the child view port. Now we're ready to create a child view port that's attached to the parent view port. We want to create a new view port within the same view group (that is, sharing the same global space) as the window's view port:

gxViewPort gcontentViewPort;

gcontentViewPort = 
GXNewViewPort(GXGetViewPortViewGroup(windowParentViewPort));

The gcontentViewPort variable now contains a valid view port that we can work with. Now we're ready to set the clip shape of this view port. The clip shape can be any geometry-based shape like a rectangle, a polygon, or a path. The clip shape for gcontentViewPort will simply be defined by the rectangle contained in viewRect, which is the portRect of the window, minus the scroll bar areas. After we set the clip shape, we dispose of the shape because it's no longer needed, thereby freeing up space within the QuickDraw GX heap:

gxRectangle contentViewPortShape;

contentViewPortShape = GXNewRectangle(&viewRect);
GXSetViewPortClip(gcontentViewPort, contentViewPortShape);
GXDisposeShape(contentViewPortShape);

Next, we need to set the mapping of gcontentViewPort to be the default mapping and attach gcontentViewPort to the parent view port:

GXSetViewPortMapping(gcontentViewPort, nil);
GXSetViewPortParent(gcontentViewPort, windowParentViewPort);

Now we need to tell QuickDraw GX which view port we want the shapes to be drawn in. We could have all shapes drawn to both view ports, but that wouldn't be especially efficient. So we make the following call, which tells QuickDraw GX to draw all the shapes we make from now on in gcontentViewPort:

SetDefaultViewPort(gcontentViewPort);

ZOOMING AND RESIZING THE CHILD VIEW PORT
Anytime the user zooms or resizes the window, we must update the clip shape of the child view port we attached to the window's parent view port. To do this, we get the portRect of the window in a fixed-point rectangle, adjust it for the scroll bars, create a new clip shape from this rectangle, and reset the clip shape of the view port to this new clip shape. The following routine does the work:

void ResetContentViewPortClip(WindowPtr theWindow)
{
    gxRectangle     viewRect;
    gxShape         contentViewPortClipShape;

    // Get the window's portRect into the fixed-point viewRect.
    GetFixedWindowBounds(theWindow, &viewRect);

    // Adjust the viewRect to accommodate the scroll bars.
    viewRect.right -= ff(kScrollBarWidth - 1);
    viewRect.bottom -= ff(kScrollBarWidth - 1);

    // Create and set the new clip shape.
    contentViewPortClipShape = GXNewRectangle(&viewRect);
    GXSetViewPortClip(gcontentViewPort, contentViewPortClipShape);
    GXDisposeShape(contentViewPortClipShape);
}

SCROLLING THE CHILD VIEW PORT'S CONTENTS
When the user scrolls in the window, we need to reset the mapping of the child view port before we call ScrollRect to scroll the bits in the window. That will ensure that when we redraw the contents of the window on the next update event, all the shapes will be located in the correct window-relative position. Otherwise they would be redrawn in their old position, because the geometry of the shapes -- which includes their position in the view port -- doesn't change.

This remapping approach gives us an advantage at print time. If we didn't adjust the mapping of the child view port, we would need to adjust the mapping of each shape. While it's possible to do that, we would have to do it again at print time to ensure that the shapes printed on the right page. If the page contained a lot of shapes, that could be a very time-consuming operation.

To update the mapping of gcontentViewPort (the child view port) to reflect the scrolling of the window, we get its current mapping, adjust it to translate the view port by the scroll amount, and set the mapping to the changed one.

gxMapping viewPortMapping;

GXGetViewPortMapping(gcontentViewPort, &viewPortMapping);
GXMoveMapping(&viewPortMapping, ff(hScroll), ff(vScroll));
GXSetViewPortMapping(gcontentViewPort, &viewPortMapping);

CREATING, MANIPULATING, AND DRAWING QUICKDRAW GX SHAPES

At this point, you've learned how to initialize QuickDraw GX and deal with view ports. It's finally time to talk about creating, manipulating, and drawing shapes.

A shape contains all the information required to draw it. To create a shape with QuickDraw GX, you simply define its geometry. Then you can draw it by calling GXDrawShape(myShape). If you haven't specified otherwise, your shape will use the default style, transform, and ink supplied by QuickDraw GX for the particular type of shape. When you change a shape's style, transform, or ink, QuickDraw GX copies a reference to the new style, transform, or ink into your shape.

To illustrate the process of creating, manipulating, and drawing shapes, in our QuickDraw GX shell we'll create a typographic shape containing text. We'll outline the text in some color and fill the inside of each letter with a pattern composed of stars. Then we'll create a typographic shape containing a line layout of some text, which we'll render in a combination of different fonts and scripts.

In the QuickDraw GX shell we'll use a picture, which we'll store in the global variable gthePage, to collect all the shapes we draw to the window. Using a picture enables us to make just one call to GXDrawShape to draw the contents of the window. We also need to set the gxUniqueItemsShape shape attribute so that each time we add a shape to the picture, QuickDraw GX will make a copy of the shape and add the copy, rather than just adding a reference to the shape. These calls create our picture and set the shape attribute:

gxShape gthePage

gthePage = GXNewShape(gxPictureType);
GXSetShapeAttributes(gthePage, gxUniqueItemsShape);

The variable gthePage now holds an empty picture, ready to have shapes added to it.

EXAMPLE 1: A SHAPE CONTAINING TEXT
First we'll create a shape containing text, which we'll store in the variable tempTextShape. We want the text to read "GX." We set the text shape's position, create the new shape, set the text size, and set the font to New York:

gxPoint textPosition = {ff(10), ff(205)};
gxShape tempTextShape;

tempTextShape = GXNewText (2, (unsigned char *) "GX", &textPosition);
GXSetShapeTextSize(tempTextShape, ff(250));

// This next call comes from the Font Library.
SetShapeCommonFont(tempTextShape, newyorkFont);

The variable tempTextShape now holds the text shape.

Outlining the text. We said that we want to outline the text with some color and fill the text shape with stars. The approach we'll take to outlining our text shape is to first convert it into a path shape, which requires only the GXSetShapeType call.

After the conversion to a path shape, each character in the text shape becomes a path. Thus, the converted shape will contain two different paths, one for each character. To draw the outline of each path, we set the fill type to gxClosedFrameFill. Then we set the pen to draw on the outside of each contour, set the pen thickness, set the color of our path to a color from the common color library, and scale it 125% on the x-axis and 65% on the y-axis so that it will come out looking short and fat.

GXSetShapeType(tempTextShape, gxPathType);
GXSetShapeFill(tempTextShape, gxClosedFrameFill);
GXSetShapeStyleAttributes(tempTextShape, gxOutsideFrameStyle);
GXSetShapePen(tempTextShape, ff(3));
SetShapeCommonColor(tempTextShape, blue);
GXScaleShape(tempTextShape, fl(1.25), fl(0.65), 0, 0);

Now we add our path shape to the picture we've stored in gthePage:

GXSetPictureParts(gthePage, 0, 0, 1, &tempTextShape, nil, nil, nil);

From now on, whenever we draw gthePage, our path shape stored in tempTextShape will be drawn as well. (See Figure 4.)

[IMAGE 041-042_Graphical_Truffl5.GIF]

Figure 4 The Path Shape Outlining Our Text

Filling with stars. To fill our shape with stars, we start by changing the fill type and color of our path. Then we define the star shape and the pattern record that will replace the style of our shape, add our new patterned path to our picture, and dispose of all the unneeded shapes.

We need to change the fill type of tempTextShape to solid fill because at this point we want to fill the contents rather than draw the outline of each path. We also want our stars to be gray, so we need to reset the color.

GXSetShapeFill(tempTextShape, gxSolidFill);
SetShapeCommonColor(tempTextShape, cold_grey);

We define the star as a polygon shape containing one contour and five points, with the default fill of evenOdd (which results in a star with a hollow pentagon inside), and we scale it by 15% to make it tiny:

gxShape starShape;
long  starGeometry[] = {1,      // One contour.
                        5,      // Five points defining the polygon.
                        ff(60), 0, ff(90), ff(90), 0, ff(30),
                        ff(120), ff(30), 0, ff(90)};

starShape = GXNewPolygons((gxPolygons *) starGeometry);
GXScaleShape(starShape, fl(0.15), fl(0.15), 0, 0);

The pattern record contains the shape to be used as the pattern, two vectors (u and v) that describe how to tile the pattern, and the pattern attribute. (See Figure 5.) We'll attach this record to the style of our path shape, replacing the default style.

[IMAGE 041-042_Graphical_Truffl6.GIF]

Figure 5 Making the Star Pattern

The bounding box of our star shape will be a fixed-point rectangle contained in the variable starShapeBounds. This information is used to define the u and v vectors of our pattern record.

gxRectangle  starShapeBounds;

GXGetShapeBounds(starShape, 0L, &starShapeBounds);

We define u and v to place the stars side by side without overlapping:

gxPatternRecord starPattern;

starPattern.u.x = 0;
starPattern.u.y = starShapeBounds.bottom;
starPattern.v.x = starShapeBounds.right + fix1;
starPattern.v.y = 0;

We set the attributes of the pattern record to gxNoAttributes and then add our star polygon shape to the pattern record:

starPattern.attributes = gxNoAttributes;
starPattern.pattern = starShape;
Finally, we add the starPattern to tempTextShape. QuickDraw GX copies a new style to the converted tempTextShape with the pattern record, replacing the default style currently referenced by tempTextShape. Anytime this shape is drawn, it will be drawn patterned with stars. We add our updated tempTextShape to the picture stored in the variable gthePage:

GXSetShapePattern(tempTextShape, &starPattern);
GXSetPictureParts(gthePage, 0, 0, 1, &tempTextShape, nil, nil, nil);

Now we can dispose of our star polygon shape because it's contained in our pattern record, which has been encapsulated into the style of our shape. We also dispose of tempTextShape because there's now a unique reference to it in our picture.

GXDisposeShape(starShape);
GXDisposeShape(tempTextShape);

The results. Our picture, gthePage, now contains two shapes. The first shape is a colored path that outlines the text "GX." The second shape is the same path filled with gray stars. When we call GXDrawShape(gthePage), all the shapes contained in our picture are drawn. The results are shown in Figure 6.

[IMAGE 041-042_Graphical_Truffl7.GIF]

Figure 6 Our Text Outlined With Color and Filled With Stars

EXAMPLE 2: A SHAPE CONTAINING A LINE LAYOUT
In a text shape, all the text can be rendered in only one font and size. In a layout shape, on the other hand, the text can be rendered in a combination of different fonts and sizes by applying multiple style runs. A layout shape can contain as many style runs as you like. A style controls the font, the size, the script (such as Roman or Arabic), run features (which pertain to swashes, ligatures, and final forms available with the specified font), justification overrides, and kerning adjustments.

To illustrate QuickDraw GX's line layout features, we'll create a layout shape containing the text "Catch the Nasty WAVE, Dude." We'll start by creating the styles to be applied in three different style runs, and then we'll define the layout shape. Finally, we'll make the whole line of text slant backward, to demonstrate that you can perform any graphical operation on typographic shapes in QuickDraw GX.

Creating the styles. We're going to apply styles to our layout shape in three different runs. The first run of text, "Catch the Nasty," will use the Hoefler Italic font; the second run, "WAVE," will use the Times Roman font; and the third run, "Dude," will use Hoefler Italic again. In the first and third runs, we'll enable a total of three of the run features available with Hoefler Italic. But before we create the styles, we need to initialize the run controls used to regulate the run features. We specify the number of run features and styles we'll use.

gxRunControls runControls;
gxRunFeature runFeatures[3];
gxStyle styles[3];

InitializeRunControls(&runControls);

Here's how we specify the style used for our first text run:

styles[0] = NewLayoutStyle((char *)"\pHoefler Italic", ff(36), 0,
&runControls, nil, 0, nil);

We want to enable two run features in our first text run: an "as" ligature in "Nasty" and swashes on the "C" and "N."

runFeatures[0].featureType = gxLigatureType;
runFeatures[0].featureSelector = gxLigatureRareOnSelector;
runFeatures[1].featureType = gxAlternateDesignsType;
runFeatures[1].featureSelector = 
    gxAlternateDesignsChanceryOnSelector;

Now we add the two run features to the style used by this run of text:

GXSetStyleRunFeatures(styles[0], 2, runFeatures);

We create the styles for the second and third text runs:

styles[1] = NewLayoutStyle((char *)"\pTimes Roman", ff(38), 0,
                                &runControls, nil, 0, nil);
styles[2] = NewLayoutStyle((char *)"\pHoefler Italic", ff(40), 0,
                                &runControls, nil, 0, nil);

The run feature used by the last run of text will enable the final form of the "e" in "Dude" and will update the style:

runFeatures[2].featureType = gxSmartSwashType;
runFeatures[2].featureSelector = gxSmartSwashLineFinalsOnSelector;
GXSetStyleRunFeatures(styles[2], 3, runFeatures);

We'll also get the swash on "D" in "Dude" because we enabled the swash capabilities in the second run feature. When we call GXSetStyleRunFeatures, we tell QuickDraw GX line layout to use all three entries in the array.

Defining the layout shape. We need to define the length of our layout (in bytes) and initialize the lengths array used to tell QuickDraw GX line layout the length of each run of text:

short totalLengthOfLayout, lengthsArray[3];

lengthsArray[0] = 15; lengthsArray[1] = 6; lengthsArray[2] = 5;
totalLengthOfLayout = 26;

We now have all the information required to define our layout shape and add it to our picture stored in the variable gthePage. Our layout shape will contain three runs of text using three different styles. Each style will use a different text size. Two different fonts will be used, and three run features will be enabled. The definition looks like this:

char        *sampleText = "Catch the Nasty WAVE, Dude";
gxPoint     layoutPosition = {ff(10), ff(65)};


tempLayoutShape =  GXNewLayout(1, &totalLengthOfLayout,
    (void *) &sampleText, 3, lengthsArray, styles, 0, nil, nil,
    nil, &layoutPosition);

GXSetPictureParts(gthePage, 0, 0, 1, &tempLayoutShape, nil,
    nil, nil);

We draw tempLayoutShape by calling GXDrawShape(tempLayoutShape). The result is shown in Figure 7. Note that "WAVE" has been kerned automatically.

[IMAGE 041-042_Graphical_Truffl8.GIF]

Figure 7 Our Line Layout Shape

Skewing the shape and cleaning up. Because QuickDraw GX treats all typographic shapes as graphics, we can perform any graphical operation on our layout shape, now stored in tempLayoutShape. We'll skew it by 125% in the x direction about its origin, move the shape 15 pixels in the x direction to put it back into the window after the skew, and move it 75 pixels in the y direction to move it away from the beginning position. Finally, we'll add the shape to our picture stored in the variable gthePage. When drawn, it will look like Figure 8.

 GXSkewShape(tempLayoutShape, fl(1.25), 0, shapePosition.x,
    shapePosition.y);
GXMoveShape(tempLayoutShape, ff(15), ff(75));
GXSetPictureParts(gthePage, 0, 0, 1, &tempLayoutShape, nil, nil,
    nil);







Figure 8 Our Line Layout Shape, Skewed

We no longer need tempLayoutShape because it's now part of our picture, so we can dispose of tempLayoutShape and our array of styles:

short   loop;

GXDisposeShape(tempLayoutShape);
for (loop = 0; loop <= 2; loop++) 
    GXDisposeStyle(styles[loop]);

Now gthePage contains three shapes. The first shape is a colored path that outlines the text "GX," the second shape is the same path filled with stars, and the third shape is a typographically cool and skewed rendition of "Catch the Nasty WAVE, Dude." When we call GXDrawShape(gthePage), all these shapes are drawn. In the QuickDraw GX shell, gthePage is set up in the CreateThePageOfGXShapes function.

BASIC PRINTING IN QUICKDRAW GX

QuickDraw GX adds quite a few new printing features, as mentioned earlier in this article. We'll briefly explore printing here by discussing methods for printing shapes, how to initialize QuickDraw GX printing, how to handle printing errors, how to implement three new print items added to the File menu, and how to finish up printing.

METHODS FOR PRINTING SHAPES
Three approaches to printing shapes are available to your application: you can send shapes to QuickDraw GX printing one by one, you can send a picture that contains all the shapes used to represent a page, or you can send a picture that contains other pictures.

Depending on your application, sending an entire picture full of information may be simplest, but if the picture contains quite a few shapes, say 20,000 to 25,000, you might suffer a performance penalty. On the other hand, sending shapes one by one is more complicated for some applications, but it may be the most efficient in terms of performance when you have lots of shapes -- more than 20,000. If you have lots of shapes in one big picture but you don't want to send them one by one, you should consider breaking the big picture into smaller pictures. (Recall that a QuickDraw GX picture is a shape that can contain other pictures.) This approach would enable you to group similar items in the same way as in MacDraw®.

You need to decide which method works best for your application. In the case of our shell, we'll send a picture for each page of our document (which conveniently consists of only one page).

INITIALIZING QUICKDRAW GX PRINTING
To initialize QuickDraw GX printing, we call InitPrinting after we create the graphics client. We then create a job, which is an extensible data structure containing all the information required to print a document. For example, a job specifies the formatting printer driver, the printer for which the job has been targeted, when the document should be printed, and the number of pages in the document.

OSErr   myQDGXPrintError;
gxJob   gDocumentJob;

myQDGXPrintError = GXInitPrinting();

if (myQDGXPrintError == noErr) 
    myQDGXPrintError = GXNewJob(&gDocumentJob);
else
// Your error-handling code here!

HANDLING PRINTING ERRORS
QuickDraw GX printing handles printing errors differently from the Printing Manager. The Printing Manager requires you to check for an error after each call. If you receive an error, you must match all your open calls with the appropriate close calls, close up the Printing Manager, and report the error to the user.

In QuickDraw GX printing, printing errors are local to the job. This gives you the ability to poll for errors after a group of routines, making your code smaller and cleaner. If you do receive an error within a group of routines, the routine that caused the error will prevent the remaining calls from executing until the error is cleared by calling GXGetJobError. You can then alert the user to the problem. All the QuickDraw GX printing errors are listed in PrintingErrors.h.

IMPLEMENTING THE NEW MENU ITEMS
Your application should implement three new items in the File menu: Print One Copy, Document Setup, and By Page Setup.

  • Print One Copy should print one copy of the document without presenting the user with any dialog other than a status dialog.
  • Document Setup should present a dialog that lets the user format the entire document, similar to the old Page Setup dialog.
  • By Page Setup should present a dialog that enables page-by-page formatting of a document (for instance, the user should be able to choose a different page size for each page in the document).

We won't take the space to reprint the code to implement Document Setup or By Page Setup here; check it out in our QuickDraw GX shell.

When the user chooses Print One Copy from the File menu, we want to print the application's document. In our QuickDraw GX shell, that means printing the contents of gthePage:

OSErr DoPrintOneCopy(WindowPtr window)
{
    Str255      windowTitle;
    OSErr           printError;

    if (window) 
    {
        GetWTitle(window, windowTitle);

        // Start sending the job. The job has the same name as our
        // window, and it contains one page. The name appears in the
        // status dialog.
        GXStartJob(gDocumentJob, windowTitle, 1);

        // Send the entire page down to the printer. (All the shapes
        // that are being printed have been collected into gthePage.)
        GXPrintPage(gDocumentJob, 1, GXGetJobFormat(gDocumentJob, 1),
                        gthePage);

        // This call tells QuickDraw GX printing we've finished
        // sending the job, so terminate the transmission (that is,
        // the connection to the printer).
        GXFinishJob(gDocumentJob);
        if (GXGetJobError(gDocumentJob) != noErr)
            // Your error-handling code here!
    }
}

FINISHING UP
Once we've finished printing, we leave the printing system open. This isn't a problem, because the QuickDraw GX printing system has a very small memory requirement when not in use -- approximately 35K. The main reason we leave it open is that we want to keep the job around, ready to be used the next time the user prints. If we were to close the printing system, we would need to recreate the job.

CLOSING UP THE QUICKDRAW GX WORLD

Closing up the QuickDraw GX world is as straightforward as initializing it. You should dispose of all the QuickDraw GX objects you created while your application was running. For example, if you called the GXNewShape routine to create a shape, you should call the GXDisposeShape routine to dispose of it.

In our QuickDraw GX shell's shutdown, we need to dispose of the QuickDraw GX picture we created to contain all the shapes drawn to our window, the common color library, our window, and the graphics client. To dispose of the picture we created, we first test to make sure it contains something. If it does, we call GXDisposeShape on it:

if (gthePage != nil) GXDisposeShape(gthePage); 

The QuickDraw GX shell has also been using the common color library, which we dispose of by calling

DisposeCommonColors();

Now we can dispose of the window; this also disposes of the view ports. In our shell, we created our own pointer to the window record, so we need to dispose of it with the appropriate call.

To close up the QuickDraw GX printing system, we dispose of the job and then call GXExitPrinting before we dispose of the graphics client. If we were going to save our picture to disk, we would want to flatten the job to disk as well.

if (GXDisposeJob(gDocumentJob))
    // Your error-handling code here!

if (GXExitPrinting())
    // Your error-handling code here!

Finally, we need to call GXDisposeGraphicsClient to dispose of the graphics client we created earlier:

GXDisposeGraphicsClient(gGraphicsClient);

If for some reason you haven't yet disposed of all the QuickDraw GX objects you've created while your application has been running, GXDisposeGraphicsClient disposes of them. If you enabled graphics notices earlier in your application, you'll receive a graphics notice for the first QuickDraw GX object you didn't dispose of. For example, if you didn't dispose of a shape, you'll receive this notice:

GRAPHICS NOTICE: shape not disposed

This isn't a problem when you're shutting down your application, because the QuickDraw GX heap is cleaned up when GXDisposeGraphicsClient is called. But getting a notice like this when your application ends means that you've been leaving unused items in the QuickDraw GX heap. That could mean reduced performance, since the QuickDraw GX Memory Manager has to page these unused objects into and out of the heap in a low-memory situation.

If you do receive a graphics notice about something you didn't dispose of, you should track down the object to improve your runtime memory management. Prevent your application from calling GXDisposeGraphicsClient so that the QuickDraw GX heap doesn't disappear before you can analyze it. In GraphicsBug, use the Heap menu to choose your application's heap; then type "hd" to get a dump of your QuickDraw GX heap, or "hd shape" to get a list of all the shapes you forgot to throw away.

In our QuickDraw GX shell, the routine ShutDownProgram closes up the QuickDraw GX world.

WHERE TO GO FROM HERE

We've taken a look at what you need to do to begin making use of the new QuickDraw GX technology. Now you're ready to tackle that huge pile of QuickDraw GX material.

I suggest starting with the sample code that comes with QuickDraw GX. There are sample applications ranging from very simple to relatively complex, and some tools to play with. Try out the samples that interest you. For an illustration of the minimum number of lines of code needed to draw GX shapes, take a look at the "One Rectangle" sample. It initializes the QuickDraw GX world, attaches a view port to a window, creates a rectangle, and draws the shape to the window. Then take a look at the QuickDraw GX documentation. You should start with the "QuickDraw GX Objects" chapter, which describes all the objects illustrated in Figure 2. From there, proceed to the parts of the documentation that make the most sense for your application.

I hope you've enjoyed this overview of QuickDraw GX. We haven't covered all the exciting parts of the technology, by any means. This is only the beginning!

MANAGING MEMORY IN THE QUICKDRAW GX HEAP

The graphics and layout portions of QuickDraw GX use their own heap format and their own relocating Memory Manager to improve efficiency. Why is this necessary? The GX system is object based, so it needs the ability to quickly move graphics and layout objects from the heap onto disk when they aren't needed and additional memory is required, and to move these objects back into memory when they're needed. This isn't possible with the current Memory Manager.

Your application has quite a bit of control over the objects it creates in the QuickDraw GX heap. The QuickDraw GX API provides calls to unload objects from the heap, and shape attributes that indicate when a shape should be paged out of the heap to the backing store on disk. If you don't unload the objects or set any of the shape attributes yourself, QuickDraw GX unloads the oldest objects first.

You can use a GXUnload call to tell QuickDraw GX to page a graphics or layout object -- specifically, a shape, style, transform, ink, color profile, color set, or tag -- from the heap to the backing store on disk when the next memory management call occurs. For example, to unload a particular shape from the heap, you would use the following call:

GXUnloadShape(gxShape target);

QuickDraw GX maintains a reference to the objects, so it can read them from disk back into the heap if your application needs them. It does this automatically, but if you want to improve the performance of your application, you can explicitly load graphics and layout objects with a GXLoad call. For example, to load a particular shape into the heap, you would use the following call:

GXLoadShape(gxShape target);

There are two shape attributes that you can set to indicate when to page a shape out to disk when memory is needed:

  • When gxDiskShape is set, this shape will be the first shape to be paged out of memory to disk when memory is needed.
  • When gxMemoryShape is set, this shape will be the last to be paged out to disk.

It's generally a good idea to dispose of an object as soon as you've finished with it. If you leave a bunch of unused objects lying around in the heap, QuickDraw GX will maintain them and page them out to disk when memory becomes tight. This wasted operation will cause your application to take a performance hit.

If you have a collection of shapes, say a picture, that you won't need for a while, you should consider flattening them to disk and then disposing of them. When you flatten a shape to disk, each object is compressed and sent to the file as a stream of data. When you dispose of the objects you just flattened, you free up some heap space.

TRACKING DOWN A MEMORY TRASHING PROBLEM

Validation is a good way to track down a memory trashing problem. If the QuickDraw GX heap gets trashed, you need to know if it's your application that's doing the damage or if it's QuickDraw GX. These validation levels can help you find this out:
gxNoMemoryManagerValidation = 0x0000
gxApBlockValidation         = 0x0100
gxFontBlockValidation       = 0x0200
gxApHeapValidation          = 0x0400
gxFontHeapValidation        = 0x0800
gxCheckApHeapValidation     = 0x1000
gxCheckFontHeapValidation   = 0x2000

To see if the problem is caused by your application, add gxApHeapValidation or gxFontHeapValidation to the types of validation you set. The one you choose depends on whether you believe the problem lies in a general API call or within the font heap (the heap separate from the QuickDraw GX heap where all the font information is cached). This type of validation checks the public API calls when they make a memory request.

If your application doesn't trash memory after adding this validation, it means that the problem is probably caused by an internal call within QuickDraw GX. In that case, add the validation level gxCheckApHeapValidation or gxCheckFontHeapValidation. This additional validation checks the memory allocation for the internal core calls. If you die at this point, you know that the internal call is causing the problem.


PETE ("LUKE") ALEXANDER started out as a meteorology major in college and to this day is great at forecasting the weather. He'd be happy to see nothing but blue skies, and in all kinds of weather he strives to incorporate something blue in his attire. He loves raw carrots, despises cooked ones. He hates waiting in lines and wearing a wristwatch. Sometimes when he's driving down the street and spots someone sitting at a bus stop, he'll honk his horn to wake them from their dazed stupor. And when he sees bicyclists sitting in their bike seats while riding up hills, he's been known to yell out the window, "Stand up, you weenie!"*

If you're a PostScript language whiz making the transition to QuickDraw GX, you'll find the article "QuickDraw GX for PostScript Programmers" in this issue helpful.*

Shapes are completely resolution independent, which enables accurate representation at any size to the screen or printer. *

The By Page Setup and Document Setup dialogs are new in QuickDraw GX. They're described later in this article under "Basic Printing in QuickDraw GX."*

For a complete list of all the errors, warnings, and notices provided by the graphics and layout portions of QuickDraw GX, take a look at the graphics errors.h header file. *

Routine naming in QuickDraw GX is very predictable and logical. All calls preceded by "GX" are from the core API, while ones without "GX" are from the library or application code. In addition, all calls pertaining to the same object are very similar, and once you grasp how to operate on one object, you pretty much know how to operate on all objects. *

If you're looking for a shortcut, you can skip the InitCommonColors call, because your first call to SetShapeCommonColor will initialize the library. *

The number of view ports that can be attached to another view port is limited only by the amount of memory you make available in the QuickDraw GX heap. *

QuickDraw GX uses fixed-point numbers, offering the advantage of speed, instead of the floating-point numbers used by QuickDraw. The ff macro uses IntToFixed to convert an integer into a fixed-point number; another handy macro, fl, converts a floating-point number into a fixed-point number.*

Another approach to outlining text, described in the article "QuickDraw GX for PostScript Programmers" in this issue, involves using text faces. *

THANKS TO OUR TECHNICAL REVIEWERS Hugo Ayala, Cary Clark, Tom Dowdy, Dave Hersey, Daniel Lipton, Dave Opstad*

 

Community Search:
MacTech Search:

Software Updates via MacUpdate

Latest Forum Discussions

See All

Whitethorn Games combines two completely...
If you have ever gone fishing then you know that it is a lesson in patience, sitting around waiting for a bite that may never come. Well, that's because you have been doing it wrong, since as Whitehorn Games now demonstrates in new release Skate... | Read more »
Call of Duty Warzone is a Waiting Simula...
It's always fun when a splashy multiplayer game comes to mobile because they are few and far between, so I was excited to see the notification about Call of Duty: Warzone Mobile (finally) launching last week and wanted to try it out. As someone who... | Read more »
Albion Online introduces some massive ne...
Sandbox Interactive has announced an upcoming update to its flagship MMORPG Albion Online, containing massive updates to its existing guild Vs guild systems. Someone clearly rewatched the Helms Deep battle in Lord of the Rings and spent the next... | Read more »
Chucklefish announces launch date of the...
Chucklefish, the indie London-based team we probably all know from developing Terraria or their stint publishing Stardew Valley, has revealed the mobile release date for roguelike deck-builder Wildfrost. Developed by Gaziter and Deadpan Games, the... | Read more »
Netmarble opens pre-registration for act...
It has been close to three years since Netmarble announced they would be adapting the smash series Solo Leveling into a video game, and at last, they have announced the opening of pre-orders for Solo Leveling: Arise. [Read more] | Read more »
PUBG Mobile celebrates sixth anniversary...
For the past six years, PUBG Mobile has been one of the most popular shooters you can play in the palm of your hand, and Krafton is celebrating this milestone and many years of ups by teaming up with hit music man JVKE to create a special song for... | Read more »
ASTRA: Knights of Veda refuse to pump th...
In perhaps the most recent example of being incredibly eager, ASTRA: Knights of Veda has dropped its second collaboration with South Korean boyband Seventeen, named so as it consists of exactly thirteen members and a video collaboration with Lee... | Read more »
Collect all your cats and caterpillars a...
If you are growing tired of trying to build a town with your phone by using it as a tiny, ineffectual shover then fear no longer, as Independent Arts Software has announced the upcoming release of Construction Simulator 4, from the critically... | Read more »
Backbone complete its lineup of 2nd Gene...
With all the ports of big AAA games that have been coming to mobile, it is becoming more convenient than ever to own a good controller, and to help with this Backbone has announced the completion of their 2nd generation product lineup with their... | Read more »
Zenless Zone Zero opens entries for its...
miHoYo, aka HoYoverse, has become such a big name in mobile gaming that it's hard to believe that arguably their flagship title, Genshin Impact, is only three and a half years old. Now, they continue the road to the next title in their world, with... | Read more »

Price Scanner via MacPrices.net

B&H has Apple’s 13-inch M2 MacBook Airs o...
B&H Photo has 13″ MacBook Airs with M2 CPUs and 256GB of storage in stock and on sale for up to $150 off Apple’s new MSRP, starting at only $849. Free 1-2 day delivery is available to most US... Read more
M2 Mac minis on sale for $100-$200 off MSRP,...
B&H Photo has Apple’s M2-powered Mac minis back in stock and on sale today for $100-$200 off MSRP. Free 1-2 day shipping is available for most US addresses: – Mac mini M2/256GB SSD: $499, save $... Read more
Mac Studios with M2 Max and M2 Ultra CPUs on...
B&H Photo has standard-configuration Mac Studios with Apple’s M2 Max & Ultra CPUs in stock today and on Easter sale for $200 off MSRP. Their prices are the lowest available for these models... Read more
Deal Alert! B&H Photo has Apple’s 14-inch...
B&H Photo has new Gray and Black 14″ M3, M3 Pro, and M3 Max MacBook Pros on sale for $200-$300 off MSRP, starting at only $1399. B&H offers free 1-2 day delivery to most US addresses: – 14″ 8... Read more
Department Of Justice Sets Sights On Apple In...
NEWS – The ball has finally dropped on the big Apple. The ball (metaphorically speaking) — an antitrust lawsuit filed in the U.S. on March 21 by the Department of Justice (DOJ) — came down following... Read more
New 13-inch M3 MacBook Air on sale for $999,...
Amazon has Apple’s new 13″ M3 MacBook Air on sale for $100 off MSRP for the first time, now just $999 shipped. Shipping is free: – 13″ MacBook Air (8GB RAM/256GB SSD/Space Gray): $999 $100 off MSRP... Read more
Amazon has Apple’s 9th-generation WiFi iPads...
Amazon has Apple’s 9th generation 10.2″ WiFi iPads on sale for $80-$100 off MSRP, starting only $249. Their prices are the lowest available for new iPads anywhere: – 10″ 64GB WiFi iPad (Space Gray or... Read more
Discounted 14-inch M3 MacBook Pros with 16GB...
Apple retailer Expercom has 14″ MacBook Pros with M3 CPUs and 16GB of standard memory discounted by up to $120 off Apple’s MSRP: – 14″ M3 MacBook Pro (16GB RAM/256GB SSD): $1691.06 $108 off MSRP – 14... Read more
Clearance 15-inch M2 MacBook Airs on sale for...
B&H Photo has Apple’s 15″ MacBook Airs with M2 CPUs (8GB RAM/256GB SSD) in stock today and on clearance sale for $999 in all four colors. Free 1-2 delivery is available to most US addresses.... Read more
Clearance 13-inch M1 MacBook Airs drop to onl...
B&H has Apple’s base 13″ M1 MacBook Air (Space Gray, Silver, & Gold) in stock and on clearance sale today for $300 off MSRP, only $699. Free 1-2 day shipping is available to most addresses in... Read more

Jobs Board

Medical Assistant - Surgical Oncology- *Apple...
Medical Assistant - Surgical Oncology- Apple Hill Location: WellSpan Medical Group, York, PA Schedule: Full Time Sign-On Bonus Eligible Remote/Hybrid Regular Apply Read more
Omnichannel Associate - *Apple* Blossom Mal...
Omnichannel Associate - Apple Blossom Mall Location:Winchester, VA, United States (https://jobs.jcp.com/jobs/location/191170/winchester-va-united-states) - Apple Read more
Cashier - *Apple* Blossom Mall - JCPenney (...
Cashier - Apple Blossom Mall Location:Winchester, VA, United States (https://jobs.jcp.com/jobs/location/191170/winchester-va-united-states) - Apple Blossom Mall Read more
Operations Associate - *Apple* Blossom Mall...
Operations Associate - Apple Blossom Mall Location:Winchester, VA, United States (https://jobs.jcp.com/jobs/location/191170/winchester-va-united-states) - Apple Read more
Business Analyst | *Apple* Pay - Banco Popu...
Business Analyst | Apple PayApply now " Apply now + Apply Now + Start applying with LinkedIn Start + Please wait Date:Mar 19, 2024 Location: San Juan-Cupey, PR Read more
All contents are Copyright 1984-2011 by Xplain Corporation. All rights reserved. Theme designed by Icreon.