Macintosh Q & A

Q I'm using the TPopup class in MacApp 3.0.1 in my window and I want to underline
the title string of a pop-up menu programmatically. The title text style is stored in a
field in the class but is used only when the pop-up menu is first created. How can I
change the text style of a pop-up menu title after it has been created?

A MacApp's TPopup class is basically just a wrapper around System 7's Popup CDEF
(with its own CDEF for pre-System 7) and so is subject to the same limitations as
normal System 7 pop-up menus. You're correct that the title style is stored in the
TPopup class and referenced only once, when the pop-up control is created. What
happens is that when a pop-up menu is created with NewControl, the Popup CDEF
interprets the value parameter to NewControl to be the title style of the pop-up menu
control. Thereafter, the value of the control is equal to the currently selected menu
item. TPopup::CreateCMgrControl calls NewControl as follows:

ControlHandle aCMgrControl = NewControl(itsPort, qdArea, itsTitle,
      FALSE, (short) this->GetPopupTitleStyle(), fMenuID,
      fItemOffset, this->GetProcID(), fUseAddResMenuResType);

The important setting is the title style: notice the TPopup::GetPopupTitleStyle call,
which returns a short integer corresponding to the text style settings. The problem is
that there's no way of defining this title style after the control has been created, so you
have to recreate the control when you want to change the title style. This may seem a
bit much, but it takes only a few lines of code. The important thing to remember is that
most of the information you need is already part of TPopup; all you're doing is
recreating the control.

Dispose of the old control, set the fTitleStyle field to the title style you want, and then
call CreateCMgrControl to create a new control with this title style, using all the
characteristics already set in your TPopup object. Here's the code to do this:

CStr255   itsLabel;
short      itsVal;

/* First free the old control. */
DisposeControl(myPopup->fCMgrControl);
myPopup->fCMgrControl = NULL;
/* Now set the pop-up title style to underline. */
myPopup->fTitleStyle = myPopup->fTitleStyle + underline;
/* Get title and current value to send to CreateCMgrControl. */
myPopup->GetMenuLabel(itsLabel);
itsVal = myPopUp->GetCurrentItem();
/* Now create a new control with the desired text style. */
myPopup->CreateCMgrControl(itsLabel, itsVal, 0, 0, 0);

 

Q I'm having a problem with Balloon Help, getting HMCompareItem to work properly.
I've got several menu items that can change dynamically, and while HMCompareItem
successfully finds the first item, all other items have no balloons. What's the problem
here?

A The problem is that the match string isn't exact. HMCompareItem only finds exact
matches for the actual menu items. (A common case to look out for is ellipsis (...)
versus three periods (...): always use an ellipsis in menus.)

If you can't determine the exact match ahead of time, we suggest that you use a
different technique: modify the help string on the fly. A method that other developers
have used is to store the current menu state in their preferences file along with the
current menu help string; then, as the application changes menu items, they modify
the 'STR ' resource that the help item refers to on the fly.

Q I'm writing a QuickDraw GX printer driver and need to get the text size of a shape.
I've tried GXGetStylePenSize and GXGetShapePenSize, but these continuously send back
12 no matter what the real size is. I've looked through the shapes in GraphicsBug, and
12 is there for the text size. What can I do to get the correct size?

A QuickDraw GX has three different shapes to handle typography -- text, glyph, and
layout -- and each one stores the typographic style objects (which are what you need)
differently. (There's a good discussion of the three types of typographic shapes in
QuickDraw GX: Programmer's Overview on pages 97 through 115.)

The important thing to remember is that simple text shapes can have only one type
style (attached to the style attribute of the object), so they're fairly easy to work
with. However, glyph and layout shapes can have one or more runs of type styles
(attached to the style list attribute of the object's geometry), so they can be more
complex to work with. Only if a glyph or layout shape doesn't have a style list attached
to its geometry is the style attribute of the object itself used. For shapes with multiple
style runs, there's no simple answer to the question "What is the text size of this
object?"

For glyph and layout shapes, you'll need to write a "GetSizes" function that's capable of
returning one or more sizes. This routine should get the style list by calling
GXGetGlyphShapeParts or GXGetLayoutShapeParts. If the style list is nil, return the
default size in the style attribute of the object itself; otherwise, return an array of
each size in the list of styles, or whatever is appropriate for your application.

Q I'm writing a QuickDraw GX application, and the glyphs that are drawn on the screen
sometimes don't match the character codes in the shape. Any idea what's going on?

A It's very important to pass the correct script system, language, and platform when
your application creates a layout shape or a style used within a layout shape. The
following code fragment will do the trick:

long   script;
long   language;
// Set myStyle's encoding correctly for this machine.
if ((script = GetEnvirons(smKeyScript)) != 0 &&
    (language = GetScript(script, smScriptLang)) != 0)
   GXSetStyleEncoding(myStyle, gxMacintoshPlatform,
      (gxFontScript) (script + 1), (gxFontLanguage) (language + 1));
else
   GXSetStyleEncoding(myLayoutStyle, gxMacintoshPlatform,
      gxRomanScript, gxNoLanguage);

In the case of a shape, the code is similar but calls GXSetShapeEncoding instead of
GXSetStyleEncoding. Note the "(script + 1)" and "(language + 1)": this synchronizes
the information returned by the Script Manager with QuickDraw GX's representation
of the same data.

Q When displaying JPEG-compressed PICTs with DrawPicture, I call QDError and get
the error code -8976, which isn't documented anywhere I've looked. What's going on?

A This is the codecNothingToBlitErr error. It means that the picture was drawn into an
entirely clipped-out bitmap. You can safely ignore this error. This is fixed in Apple's
Multimedia Tuner (and will be fixed in future versions of QuickTime), so that the
error won't be reported. Better, of course, would be to avoid drawing into clipped-out
bitmaps at all.

Q I'd like to add the capability to turn the Macintosh on and off automatically in my
application. Is there an API for scheduled startup and shutdown?

A Yes and no: there's an API for auto-startup, but not for timed shutdown. The
auto-startup feature is built into the Time Manager. The Power Manager features flag
has been updated so that it can easily be tested. Timed shutdown will have to be done
manually: we recommend creating a background-only application that simply waits for
the appropriate time and then issues the Finder event to shut the system down.

Auto-startup can be used if the PMFeatures routine returns a long word with bit 10
set. The name of the enum in the new headers is hasStartupTimer. If this flag is
present, these routines are also supported:

void SetStartupTimer(StartupTime *theTime);
OSErr GetStartupTimer(StartupTime *theTime);

SetStartupTimer sets the time that the Macintosh will start up from a power-off state,
and enables or disables the startup timer. On a Macintosh that doesn't support the
startup timer, SetStartupTimer does nothing. The time and enable flags are passed in
the following structure:

typedef struct WakeupTime WakeupTime, StartupTime;
struct WakeupTime {
  unsigned  long wakeTime;
                     /* startup time (same format as current time) */
  Boolean   wakeEnabled;  /* 1 = enable startup timer, 0 = disable */
  SInt8     filler;
};

GetStartupTimer returns the startup time and the state of the startup timer. If a
particular Macintosh doesn't support the startup timer, GetStartupTimer returns 0.

Q We'd like to open a (usually color) dialog with GetNewDialog such that, if indicated
by the user, it ignores the 'dctb' resource and opens in black and white instead. This is
a feature request from our users. The best solution we've got so far is to check to see
whether we want a dialog to appear in color before calling GetNewDialog. If we want to
suppress color, we patch GetResource with code that will look for any call to fetch a
'dctb' resource with the same ID as our dialog. If the patch detects such a call, it
returns a nil handle; otherwise, it calls the original GetResource trap and returns its
return value. When the GetNewDialog call is complete, we unpatch GetResource if we
had patched it. Is this a good solution?

A The method you describe -- patching GetResource -- will probably work, but is a
needlessly complex solution to the problem. In general, patching should be considered a
last resort solution, suitable only when there are no other options.

Why not just have two duplicate DLOG resources (both referencing the same DITL),
one with a corresponding 'dctb' and one without? (DLOG resources are small, on the
order of 30 bytes, so there shouldn't be a size problem.) You can then pass the
appropriate ID to GetNewDialog to get either a color or a noncolor dialog. If you want to
make this "automatic," make the DLOG resource without a corresponding 'dctb' have an
ID that's, say, 1000 more than the one that has a 'dctb'; then keep a global variable
that contains either 0 (if the user wants color) or 1000 (for black and white). When
you call GetNewDialog, add the global to the (color) dialog ID and pass the result to
GetNewDialog. Voilà! You get color if the global is 0, black and white if it's
1000. This is far cleaner and safer than patching could ever be -- and easier, too.

Q I have a question about the MailTime structure and setting the
postIt.coreData.sendTime field based on the contents of GetDateTime. The messages I'm
reading have the time and date in them, so I can retrieve the time the message was
received. I convert this to a DateTimeRec and then call Date2Secs. What's the real way
to handle this? I've written a routine that seems to be the right approach, but the time
in the mailbox is always wrong, though my location is correct in the Date & Time
control panel.

A Here's a snippet that does what you need:

void MacToMailTime(unsigned long macTime, MailTime& mailTime)
{
   long               internalGmtDelta;
   long               dlsDelta = 0;
   MachineLocation   aLocation;

   ReadLocation(&aLocation);
   internalGmtDelta = aLocation.gmtFlags.gmtDelta & 0x00ffffff;
   if (BitTst(&internalGmtDelta, 23))
      internalGmtDelta = internalGmtDelta | 0xff000000;
   mailTime.time = macTime;
   mailTime.offset = internalGmtDelta;
}

Q  I'm trying to get unread mail messages from the PowerTalk mailbox, but the
SMPGetNextLetter routine is returning an error of -903. That's a PPC Toolbox error
noPortErr! What's going on?

A The problem is that you haven't set the isHighLevelEventAware flag in the SIZE
resource for your application. This is necessary because the AOCE Standard Mail
Package routines require your application to accept high-level events.

Q How can I count the number of unread messages in the PowerTalk mailbox?

A Using the existing AOCE programming interfaces, this isn't possible. However, a new
mailbox API will be available soon (if it isn't already) that will allow these types of
operations.

Q I've been using the Standard Mail Package to add mail capability to my application. In
some circumstances we generate text or PICT files and add them as attachments to the
document we're mailing. The documentation states that the enclosure isn't actually
added until well after SMPAddAttachment has returned. I'd like to delete the files as
soon as possible after I've generated them. How do I know when the file has been
completely copied so that I can delete the original?

A The problem with SMPAddAttachment is that the process is nondeterministic. The file
is added by an asynchronous background process that's controlled by sending Apple
events between your application and the Finder. Apple events are returned to your
application during the file copy (they'll be handled by the normal Apple event
mechanism).

Generally, these are the indications that the copy is complete:

However, if you were to try to add another enclosure at this point, you might still end
up getting the kSMPCopyInProgress error.

What you need to do is treat the kSMPCopyInProgress error as a "busy, try later"
indication, and try the action again the next time through your event loop. This ensures
that the Mailer (and the Finder) get a chance to move data between successive tries.

Q I'm trying to write a patch that gets called when a floppy disk is inserted. I tried a
GNE filter patch that looks for the diskEvt message in the event queue. It works, but
it's not really what I want; the patch also gets called when any volume, not just a
floppy disk, is mounted. Also, when a floppy disk is inserted, the patch gets called after
the disk's icon shows up on the desktop, and I'd like to trigger the action before that.
Any ideas?

A The Finder always gets events before your GNE patch, which is why your patch gets
called after the icon shows up on the desktop. Instead you should patch MountVol inside
the Finder.

Q I'm attempting to use the MenuHook routine called by MenuSelect to update a status
bar with text as the user traverses menu items with the mouse. It seems that if I call
TextEdit functions directly from within the function pointed to by MenuHook,
problems occur: the mouse highlights the first item in a menu, but that item stays
highlighted no matter where you move the mouse. In other words, it seems that
MenuSelect stops working correctly or that the screen is no longer correctly updated.
Can you tell me how to fix this?

A You need to save and restore the graphics port in your MenuHook routine. Even if
you aren't explicitly changing the port yourself, the TextEdit routines will probably
leave it set to the edit record's owning port, not what it was when you started.

Q I found the following declarations in Scripts.h:

extern PASCAL Boolean IsCmdChar(const EventRecord *eventRecord,
         short test)

    FOURWORDINLINE(0x2F3C, 0x8206, 0xFFD0, 0xA8B5);

But I can't seem to find any documentation for this call. Does such documentation
exist? If this is what I think it is, it could be very useful.

A Whoops, thanks for pointing this out. That routine was introduced with System 7.
We've updated the Macintosh Technical Note "International Canceling" (TE 23)
accordingly. Here's a description of the routine:

FUNCTION IsCmdChar(keyEvent: EventRecord; testChar: CHAR): BOOLEAN;

This function tests whether the Command key is being pressed in conjunction with
another key (or keys) that could generate testChar for some combination of Command
up or down and Shift up or down. This accommodates European keyboards that may have
testChar as a shifted character, and non-Roman keyboards that will only generate
testChar if Command is down. It's most useful for testing for Command-period.

The caller passes in the event record, which is assumed by the function to be an event
record for a key-down or auto-key event with the Command key down. The caller also
passes in the character to be tested for (for example, '.'). The function returns TRUE
if testChar is produced with the current modifier keys, or if it would be produced by
changing the current modifier key bits in either or both of the following ways:

Someone has to ask this: just what are the "Miscellaneous Traps" toward the end of
Traps.h, and in particular _HFSPinaforeDispatch?

A Those few defines in Traps.h are leftover baggage:

/* Miscellaneous Traps */
   _InitDogCow               = 0xA89F,
   _EnableDogCow             = 0xA89F,
   _DisableDogCow            = 0xA89F,
   _Moof                     = 0xA89F,
   _HFSPinaforeDispatch      = 0xAA52,

0xA89F is really _Unimplemented and 0xAA52 is really _HighLevelFSDispatch. They
were possibly left there to keep system builds working -- or perhaps to keep the
build engineers amused.

Q To get started writing a raster printer driver for QuickDraw GX, I wrote a
"skeleton" driver (only 5K!) that overrides only two messages: RenderPage and
RasterDataIn. The RenderPage override merely posts debug notices before and after
forwarding the message. The RasterDataIn override also posts a debug message, then
returns immediately. So no data is sent to the printer; this is, after all, only a
demonstration driver.

A crash occurs after BuggyDriver forwards the RenderPage message but before control
returns to the RenderPage override. RasterDataIn is called one or more times before
the crash occurs. Since there's hardly any code, yet the driver still crashes, I'm
guessing that incorrect driver resources are to blame (I don't pretend that I've figured
out the 'rdip' resource yet).

A You've stumbled onto a bug in QuickDraw GX: rendering into 32-bit-deep view
devices at high resolutions causes the crash you're seeing. Since the problem is in
several of the QuickDraw GX blitters, there's no workaround short of reducing the bit
depth or the resolution, or both. This problem is fixed in version 1.1.

Q I'm patching NewControl so that I can replace the standard controls on AV and Power
Macintosh machines. This seems to work fine everywhere except in alerts. When an
alert is posted, NewControl doesn't get called until after the original control is drawn
once. Any ideas?

A The "proper" way to do this is to patch NewControl and GetNewControl to change the
procID to your CDEF's procID. This is pretty clean: the Control Manager thinks your
CDEF was the one that was always asked for. The only drawback is that you'll have to
make sure your resource file is always available and in the search path. Be sure to set
the system bit in your CDEF to avoid constant reloading.

Q I'm trying to call LMGetUnitTableEntryCount in my application, but when I compile
for PowerPC I get a link error: the function isn't in the native libraries. Is this policy
or an inadvertent omission? What can I do about it?

A This is an oversight. You'll need to create an external function in a file (say, Extra.c)
to access the low-memory global yourself (from native code only), as shown below.
When an updated library is released, you'll only have to remove the Extra.c.o file from
your link command and relink your application, not recompile it.

Your Extra.c file would be simple and look something like this:

// Compile with: PPCC -appleext on Extra.c -o Extra.c.o
// Add Extra.c.o to your PPCLink command line.
// Later, when a .xcoff file is provided by Apple, replace it with
// that, and delete your Extra.c.o file.
#if defined (powerc) || defined (__powerc)
   pascal short LMGetUnitTableEntryCount()
   {
      return *(short *)0x01D2;
   }
#endif

Of course, best of all would be to rewrite your application so that it doesn't depend on
low memory at all.

Q The cursor flickers when it's over a playing QuickTime movie. Is there any way to
stop this? I don't want to hide it completely because the user needs to access controls
elsewhere on the screen, and the movie is large, so hiding it only over the movie is
still disorienting.

A Unfortunately, there's no way to prevent the flickering cursor, short of hiding it
completely. The Macintosh doesn't have a hardware cursor, so the cursor always has to
be hidden during blits to the screen. QuickTime 2.0 improved this situation by
shielding the cursor less of the time, but it still happens.

Q Is it OK for different nodes to run different versions of AppleTalk on a network?

A You don't need to run the same version of AppleTalk for each node on the network.
However, AppleTalk versions 53 and later support AppleTalk Phase 2, so if you're
working on an application that depends on Phase 2 support (for instance, use of the
NBP wildcard character "~"), you'll need to use AppleTalk version 53 or later for all
nodes running that application.

Q When I use the LaunchApplication routine with the launchDontSwitch bit set in the
launchControlFlags field for an application that doesn't have the canBackground size
resource bit set, LaunchApplication returns 0 (noErr) but the application doesn't
launch. What gives?

A LaunchApplication doesn't return an error in this case because the application
actually is launched. But since it doesn't have the canBackground bit set and it was
launched into the background, it never gets any processor time, which means that it
doesn't initialize anything and isn't added to the Application menu. If the user
double-clicks an application that has been launched like this, it will bounce forward
and get processor time, initialize itself, and be added to the Application menu as usual.

Q A colleague of mine who is a Latin freak always calls me a "lens culinaris." What
does it mean?

A You are being called a "lentil."

These answers are supplied by the technical gurus in Apple's Developer Support
Center. Special thanks to Pete "Luke" Alexander, Mark Baumwell, Mark "The Red"
Harlan, David Hayward, Scott Kuechle, Larry Lai, Joseph Maurer, Jim Mensch, and
Nick Thompson for the material in this Q & A column.

Have more questions? Need more answers? Take a look at the Macintosh Q & A
Technical Notes on this issue's CD.