June 94 - Macintosh Q & A
Macintosh Q & A
Macintosh Developer Technical Support
Q I'm having trouble figuring out how to convert some assembly trap patches for selector-based traps into PowerPC native code. Do you have any suggestions for a clean
way of doing this, ideally so that it works on both 680x0-based machines and the
Power Macintosh?
A Currently there's no way to patch a selector-based trap with a native or fat patch. (A list of selector-based traps can be found in the back of Inside Macintosh X-Ref .)
The problem arises from the fact that each routine associated with a single
dispatch-based trap can have a different parameter list (that is, a different number of
parameters and different sizes for each parameter). Basically there's no way for
mixed mode to handle the variable stack frame sizes associated with selector-based
traps. This is the same thing that makes head/tail patching them so much of a pain in C.
We're in the process of trying to determine whether developers have a pressing need
to patch selector-based traps with native code. For now, keep all such patches in
680x0 code. If a patch to a particular routine is itself very time-intensive (which is
rarely the case), the 680x0 patch can call through to a native implementation.
Q I'm writing a QuickDraw GX print driver for a plotter and need to send initialization and termination strings to the plotter. How can I determine from my driver if I'm
printing the very first or last page, regardless of the number of copies? I'd like to
have this information in GXStartSendPage and GXFinishSendPage, respectively, so that
I can send my strings then.
A We recommend that you do any pre-first-page setup in GXOpenConnection (after forwarding) and any post-last-page teardown in GXCloseConnection (before
forwarding). (Although the documentation is a bit ambiguous on this point, you can
send information with GXBufferData and GXWriteData from GXCloseConnection, before
forwarding.) If there's some reason that this won't work for you, and you really need
the information in GXStartSendPage and GXFinishSendPage, you'll have to use global
data.
In some override before GXStartSendPage (perhaps GXImageDocument), initialize a
global page counter to 0. In your GXStartSendPage override, check this flag, and then
bump it after forwarding. If your check finds that the flag is 0, no pages of the
document have been sent yet.
To determine when the last page has printed, you'll also need to use global data.
Somewhere (again, GXImageDocument is fine) call GXCountPages, get the number of
copies from the job's 'copy' collection item, and multiply. In your GXFinishSendPage
override, compare the value you bumped in GXStartSendPage to this multiplied value.
When they're equal, you're just finishing the last page.
Q How do client and server desktop printers (shared printers) synchronize in QuickDraw GX? Specifically, what we're trying to find out is (for a server desktop
printer on one machine, and a matching client on another):
• If a user on the client machine sets a papertype in an input tray, is it
reflected on the server in the Input Trays dialog? If not, what's the correct
behavior for the client and the server machines (for example, should the
client Input Trays dialog be read-only)?
• Generally, what resources and data are transmitted between the host and
the client, and when? Is there a mechanism for controlling which resources
will be sent or kept local (preferably on a resource-by-resource basis)?
A Resources are transmitted to desktop printers in only one direction -- server to clients. Also, only resources with IDs greater than 0 are moved to the clients.
Therefore, it's appropriate to make the Input Trays dialog on clients read-only. Even
though Apple's drivers don't do this, it's the more correct approach. You can find out
whether you're on a server or a client by checking the desktop printer's 'comm'
resource; if its type identifier is 'ptsr', you're on the client; otherwise, you're on the
server.
If you need to have data sent from the client to the server, you should fetch the
resources at GXImageJob time (before forwarding) and then roll them into a job
collection item. In the appropriate communication message, look for the collection
item and use that data. Since GXImageJob is always called (shared printers or not), and
it's called on the client if you're working with shared printers, this method should
always work.
Q If I want to add additional properties to the paper stock (such as paper color), and I call AddCollectionItem(GXGetPaperTypeCollection(paper), . . . ), will that new
collection be stored in the desktop printer's configuration file, or is that something I
must manage? I seem to be losing my collection between invocations of the Trays
dialog.
A The papertype collection items you add won't be flattened to disk and stored in the desktop printer via your Trays dialog. There's no way to make collection item changes
and have them saved with the disk-based papertype, wherever it may be stored. You
need to manually save the information in your desktop printer (or some other place)
as resources, and then match that up with the papertypes when you want to use them.
You should match up the papertypes and the resources based on the names of the
papertypes.
You can still take advantage of the papertype collection to hold your paper color
information, as long as you also have the information stored on disk. The papertype
collection will be around as long as the papertype's job is around. For example, if you
load the color info from the desktop printer and put it in a papertype collection item at
despoolpage time, it stays there throughout the entire print cycle, and wherever the
job goes, it goes.
Q I'm having a problem resizing text elements in our QuickTime application. I'm trying to modify the element's size by calling SetTrackDimensions, and it seems to do
what I want for all element types except text. For text tracks, the element's bounding
box is resized correctly, but the text characters are scrunched into the upper left
corner of that rectangle, still at their original size. In other words,
SetTrackDimensions seems to scale the track bounds, but not the text characters
themselves. Any idea what's going on?
A This is a bug. As you determined, SetTrackDimensions is only changing the size of the track box, not setting the correct scaling factor or internal flags. To work around this
problem, use GetTrackMatrix to retrieve the current matrix, then ScaleMatrix to
change it, and finally SetTrackMatrix to make it take effect.
Q Do you know why "OCE Mail Enclosures" appears as a volume when I index through all volumes using PBHGetVInfo? Is there any way to filter out this "volume"?
A The reason "OCE Mail Enclosures" shows up is that it's the volume for an external file system (XFS) that AOCE installs in order to support access of letter enclosures via
FSSpecs from the mailer and other parts of the AOCE system. This enables the direct
access that the mailer provides in the enclosure fields of letters, allowing users to
manipulate enclosures like any other file in the Finder, copying and even launching
them directly from the enclosure pane.
To filter out the "OCE Mail Enclosures" volume, you should check the Finder flags of
the root directory in each volume to determine whether that volume should be visible
to the user. The Finder flags for directories are located in the ioDrUsrWds field in the
dirInfo variant of the CInfoPBRec structure. If the fInvisible bit is set, you should not
display that volume to the user. Here's a snippet:
void main(void)
HVolumeParampBlock; CInfoPBRec cBlock;
Str255 volName, fName;
OSErr err;
pBlock.ioNamePtr = volName;
err = noErr;
for (pBlock.ioVolIndex=1; err==noErr; pBlock.ioVolIndex++) {
err = PBHGetVInfo((HParmBlkPtr)&pBlock, false);
cBlock.dirInfo.ioNamePtr = fName;
cBlock.dirInfo.ioVRefNum = pBlock.ioVRefNum;
// Query the directory info ioDrDirID.
cBlock.dirInfo.ioFDirIndex = -1;
// This is the root directory.
cBlock.dirInfo.ioDrDirID = 2;
err = PBGetCatInfo(&cBlock, false);
if ((cBlock.dirInfo.ioDrUsrWds.frFlags &
fInvisible)!=0)
// It's invisible.
. . .
}
}
}
}
Q I have a dialog with two editText fields. When I populate the two fields with text, whichever field I populate first is displayed two pixels too high within its item. The
second field is fine, and it has the "focus" of the dialog. When I click in the first field,
any new text is added at the correct height, but unfortunately that's two pixels below
where the previous text was drawn. The fields are both 10-point plain Geneva, and the
editText boxes are 16 pixels high. Any ideas?
A The Dialog Manager has a bug that causes problems when you use an alternate font or size for the editText items. The problem is how it draws the text initially in the dialog:
the text for the currently active item is drawn by manipulating the dialog's TextEdit
record, and the text for all other items is drawn by calling TextBox. The solution is to
call SelIText just before you call SetIText each time you populate a field with text.
Q How can I convert an RGB color into an index to a palette created by my application? Color2Index converts the RGB color to an index to the current device's color table, but
that's not what I want.
A There's no single call that will give you a palette match to an RGB color. You'll have to do this: call Color2Index to get the closest match to your RGB request; call
Index2Color to get the device's indexed color from your match; search the palette
yourself to find the color match (according to RGB value); and call Color2Index to
verify that you have the color you're looking for.
Alternatively, you can create an off-screen GWorld, call Palette2CTab to convert your
palette to a color table, and call UpdateGWorld to insert your new color table in your
off-screen GWorld. Then, to find the index of an RGB color, make your GWorld the
active device and call Color2Index.
Q I've tried in vain to find a way to print white text on a black background. Is there a way to do this, and if so, how?
A The trick is to use the srcBic pen mode: FillRect(theRect, black);
PenMode(srcBic);
DrawString(myString);
Q In our application, the user can select an area of an image and drag it around. I want to show this visually by inverting the region under the current mouse coordinate as
the user moves the mouse around. Inverting the region is nice because I can invert it
again to get the unselected pixels back. It's not nice, however, in that a 50% gray color
looks the same when it's inverted. To fix this problem, I tried using PaintRgn with an
RGBForeColor of r,g,b = 0x8000 and a transfer mode of addOver. This works great on
24-bit screens, but it seems that on 256-color screens, applying this operation twice
doesn't quite return to the original color. Am I going to have to use a custom color
search procedure?
A You get the results you want on direct devices but not on indexed ones, and unless you're extremely lucky with your color table, this is how it will always work. The
problem is that the mode calculations are done with the actual RGB values used (the
ones available in the color table), not the ones you request. On indexed devices there's
almost always a difference between the two, so unless your color table happens to have
the exact color you request, there will be "errors." This never happens on direct
devices because all colors are available -- the operations work on direct RGB values
and are never mapped through color tables.
The solution is either to set up your color tables or palettes to make sure you get the
results you want each time, or to install a custom color search procedure if that's what
you'd prefer.
Q After we call CMOpen and a connection is established, a dialog is displayed and eventually goes away. Unfortunately, the C++ object framework we use is bombing
because it's getting a deactivate event for that window, which belongs to the
Communications Toolbox. We wrote a kludge that sets a flag after the call to CMOpen is
finished and eats the deactivate event if the flag is set. Is there a better way for us to
tell whether to let the class library handle the event or to handle it ourselves?
A A window or dialog created by a connection tool has the connection record handle stored in the refCon field. The sequence, then, is to check the event record to find out if
the event is tied to operations in a window and, if so, check the window's refCon against
your connection handles. If there's a match, call CMEvent for that event; otherwise
pass it on to the framework. You'll probably need to write a handler for your class
library to do this properly, overriding the default window-handling routines for this
special case.
Q When our application opens a Communications Toolbox tool, we issue a CMOpen with the asynchronous flag true and go into a loop, calling CMIdle and then CMStatus until
we see the cmStatusOpening flag go down or the cmStatusOpen flag come up. When we
use the Express Modem Tool (on a Macintosh Duo 230), those flags never change.
Should we be doing something different or is there a problem with that tool?
A The Express Modem Tool uses a background process (coupled tightly with the hardware implementation) to actually move data. Unless your application yields
processor time through the WaitNextEvent cycle, the background process is stuck
when you call the tool asynchronously. (The synchronous call has been massaged to
give the process time, of course.)
What you're doing, essentially, is making the asynchronous call synchronous by
trapping your application in this kind of loop. The proper thing to do would be either to
use the call synchronously or to continue to use it asynchronously but exit back to the
main event loop and look for the flags from there. When the appropriate flag is set, you
can then dispatch off to a handler routine. Even better, use a completion routine to
notify the application that the CMOpen has completed and obtain the function result
from the ConnHandle errors field.
Q I've implemented a variant of the CMChoose dialog based on the Choose.p sample code in Inside the Macintosh Communications Toolbox, page 323. The problem I have is that
all the fields of the dialog appear in 12-point Chicago rather than the 9-point Geneva