September 95 - Macintosh Q & A
Macintosh Q & A
Q How do I create a menu with an icon as its title? A Set the menu title to 0x0501handle, where handle is the result of calling GetIconSuite. An example snippet of code follows; this code assumes that the menu title
is already five bytes long.
void ChangeToIconMenu()
Handle theIconSuite = nil;
MenuHandle menuHandle;
GetIconSuite(&theIconSuite, cIcon, svAllSmallData);
menuHandle = GetMenuHandle(mIcon);
// Second byte must be 1, followed by the icon suite handle.
(**menuHandle).menuData[1] = 0x01;
*((long *)&((**menuHandle).menuData[2]))
= (long)theIconSuite;
// Update display (typically you do this on startup).
DeleteMenu(mIcon);
InsertMenu(menuHandle, 0);
InvalMenuBar();
}
}
}
Q We're drawing palette icons with a loop that consists basically of the following: GetIcon...
HLock...
CopyBits...
ReleaseResource...
Our native PowerPC version seems to draw these icons a lot more slowly than even our
680x0 version running under emulation, which suggests a Mixed Mode Manager
slowdown. Which of these routines are currently native on the PowerPC? GetIcon and
ReleaseResource together seem to take over 90% of the time.
A As you suspected, the Resource Manager calls you're using aren't native, and they generally call File Manager routines, which aren't native either. But be careful:
what's native and what isn't is changing over time, and you shouldn't design your
application based on today's assumptions.
That said, relying on the Resource Manager to be fast is generally not a good idea,
native or not. One approach is to "cache" the icons, making sure they're in RAM at all
times. (In general, you should do this for any user interface element that will be
redrawn repeatedly while your application is open.) You can load your icons as one of
the first few operations in your initialization code, just after calling MaxApplZone
(possibly moving them high and locking them, since you don't want them to move
during a CopyBits operation). This technique yields very good performance on the
redraws that the palette needs, in exchange for a few kilobytes of memory. Don't forget
to mark the resources as nonpurgeable.
Even better, if it will suit your purposes, would be to use the Icon Utilities to retrieve
and draw your icons (as documented in Inside Macintosh: More Macintosh Toolbox) and
to build an icon cache. Using the Icon Utilities helps your application do the right thing
for different screen depths. Also, the icon-drawing routines have been optimized to
perform well under a variety of conditions.
Q How can we detect that our application is already running and bring it to the front?
A Simply iterate through the currently running processes with GetNextProcess, calling GetProcessInformation for each one and comparing its process signature with
your application's (for an example, see the article "Scripting the Finder From Your
Application" in develop Issue 20, page 67, Listing 1). If your application is running,
call SetFrontProcess to bring it to the front.
Q WindowShade is causing a problem for our application, which saves the window position and size when it saves a document to disk. If our application's windows are
"rolled up" with WindowShade, its windows appear to have zero height. Is there any
way to determine whether a window is rolled up? If so, can we determine its true size
and the global coordinates of the top left corner of the content region, so that we can
restore and reposition the window when the document is reloaded from disk?
A When WindowShade "rolls up" a window, it hides the content region of the window. You can tell a window is rolled up when its content region is set to an empty
region and its structure region is modified to equal the new "shaded" window outline.
WindowShade doesn't do anything with the graphics port, though, so if you need to store
the window's dimensions before closing it, use the window's portRect.
With regard to the window's position, WindowShade modifies the bottom coordinates of
the structure and content regions of the window, but the top, left, and right coordinates
are not changed. These are global coordinates, so you can use the top and left
coordinates to track and save the global position of the window on the screen regardless
of whether the window is rolled up.
Q Sometimes balloons won't show up when I call HMShowBalloon; I get a paramErr (-50) instead. The hmmHelpType is khmmTEHandle. HMShowBalloon calls TextWidth
on the hText of my TEHandle (the result of which is 1511, the width of 338
characters), then multiplies that by the lineHeight (12), yielding 18132. It then
compares this to 17000, doesn't like the result, puts -50 into a register, and backs
out of everything it has done previously. What's the Help Manager doing?
A The Help Manager checks against 17000 to ensure that the Balloon Help window will always be smaller than a previously determined maximum size. Currently, you're
limited to roughly the same number of characters with a styled TEHandle as you are
with a Pascal string: 255 characters.
Keep your help messages small by using clear, concise phrases. If you absolutely need
more text in a balloon, you can create a picture of it and use khmmPict or
khmmPictHandle to specify it for your help message. This is not recommended,
however; "picture text" has the disadvantage of being difficult to edit or translate to
other languages.
Q Is there any way I can stop LClick from highlighting more cells when the user drags the cursor outside the list's rView area? My program allows users to select
more than one item from a list and then drag and drop these selected items into another
list. But I run into a problem with the LClick function: when I drag these items outside
the list's rView area, it still highlights other cells. What can I do?
A If you want to use LClick and not change the highlighting of cells when the cursor leaves the rView of the list, you should install a click-loop procedure that
tracks the mouse. When the mouse is outside your list's rectangle, return false to tell
the List Manager that the current click should be aborted. It turns out that this is a
nice way to start a drag as well, since you know that the mouse has left the rectangle. It
might look like this:
GetMouse(&localPt);
if (PtInRect(localPt, &(*list)->rView) == false)
return false; // We're out of the list.
else
return true;
Q I'm developing a Color QuickDraw printer driver and want to match colors using ColorSync 1.0.5 with a custom CMM. I was told that for efficiency I should manually
match colors inside my Stdxxx bottlenecks, instead of calling DrawMatchedPicture. Is
this really more efficient? Why?
A Surprising as it may be, it is more efficient for printer drivers to manually match colors inside Stdxxx bottlenecks than to call DrawMatchedPicture. This is
because ColorSync 1.0's DrawMatchedPicture doesn't use bottlenecks as you expected.
It does install a bottleneck routine that intercepts picture comments (so that it can
watch the embedded profiles go by), but it doesn't do the actual matching in bottleneck
routines. Instead, it installs a color search procedure in the current GDevice. Inside
the search procedure, each color is matched one at a time.
While this implementation has some advantages, it's painfully slow on PixMaps,
because even if the PixMap contains only 16 colors, each pixel is matched
individually. This has been changed in ColorSync 2.0. To boost performance, PixMaps
(which are, after all, quite common) are now matched in the bottlenecks instead of
with a color search procedure. (See the Print Hints column in this issue of develop for
more on ColorSync 2.0.)
Q I need to add some PostScript comments to the beginning of the PostScript files generated by the LaserWriter GX driver. On page 4-119 of Inside Macintosh:
QuickDraw GX Printing Extensions and Drivers, it says that you can override the
GXPostScriptDoDocumentHeader message to do this. I wrote a QuickDraw GX printing
extension to implement this, assuming that all I had to do was to override the
GXPostScriptDoDocumentHeader message and buffer the desired data with
Send_GXBufferData. Here's an example of my code:
OSErr NewPostScriptDoDocumentHeader
(gxPostScriptImageDataHdl hImageData)
OSErr theStatus = noErr;
char dataBuffer[256];
long bufferLen;
strcpy(dataBuffer, "%%DAVE'S TEST DATA");
bufferLen = strlen(dataBuffer);
theStatus = Send_GXBufferData((Ptr) dataBuffer, bufferLen,
gxNoBufferOptions);
if (theStatus != noErr)
return theStatus;
theStatus = Forward_GXPostScriptDoDocumentHeader(hImageData);
return theStatus;
}
Unfortunately, this causes a bus error when Send_GXBufferData is called, even if I put
Send_GXBufferData after the call to Forward_GXPostScriptDoDocumentHeader. Why
doesn't this work?
A The override in your extension is basically correct, but the order of your code needs to be slightly different: