Retired Document
Important: This sample code may not represent best practices for current development. The project may use deprecated symbols and illustrate technologies and techniques that are no longer recommended.
AuntieDialog.html
<HTML> |
<HEAD> |
<TITLE>AuntieDialog</TITLE> |
</HEAD> |
<BODY BGCOLOR="#FFFFFF"> |
<P><TABLE BORDER=0 CELLSPACING=0 CELLPADDING=10 WIDTH=450> |
<TR> |
<TD> |
<H1>AuntieDialog</H1> |
<P>Pete Gontier<BR> |
Apple Carbon High Level Toolbox Engineering<BR> |
© 1999-2000 Apple Computer, Inc.</P> |
<H2> |
<HR> |
<BR> |
Introduction</H2> |
<P>AuntieDialog is a module, provided in source code form, |
which replaces some key Dialog Manager calls with calls to |
Control Manager and Window Manager, thus freeing you from |
struggling with the Dialog Manager API, yet allowing you to |
use the familiar tools you've always used to build |
dialogs.</P> |
<H2>But Why?</H2> |
<P>For several squillion years, developers have chafed at |
the limitations of the Dialog Manager. In fact, there's an |
entire Technote about how badly the Dialog Manager sucks. |
The Technote suggests that rather than attempt to bludgeon |
the Dialog Manager into working the way you need it to, you |
should use Window Manager and Control Manager. That's a fine |
suggestion, except that popular dialog creation tools assume |
you'll be using the Dialog Manager to manage your dialogs, |
and there are no Human Interface Toolbox (HIT) calls outside |
of Dialog Manager which read dialog resources.</P> |
<H2>Recommended Uses</H2> |
<P>AuntieDialog is best suited for use by new code, although |
in the long run you might be better off investing the time |
in getting rid of your existing Dialog Manager code as well. |
(For you conspiracy theorists out there, this does not mean |
Apple is considering abolishing the Dialog Manager, as much |
as we might like to.) Another thing AuntieDialog does not |
imply is that you should abandon the use of such high-level |
calls as <CODE>StandardAlert</CODE>. Dialog Manager is |
perfect for use by such high-level calls, which present a |
simple user interface. Dialog Manager's limitations really |
begin to show when you try to build a more complicated user |
interface on top of it.</P> |
<H2>Limitations</H2> |
<P>AuntieDialog is not a Dialog Manager replacement. |
Remember, Dialog Manager sucks. There'd be no point in |
slavishly emulating it. Instead, AuntieDialog offers more |
flexibility and fewer limitations, but you're not going to |
be able to drop this code into your project and blow away |
all instances of <CODE>#include <Dialogs.h></CODE> |
without doing some work.</P> |
<P>For example, AuntieDialog provides no way to designate a |
window as system-modal. (In fact, there's never been a way |
to make an arbitrary window system-modal; the fact that |
AuntieDialog cannot be used with <CODE>ModalDialog</CODE> |
just makes the problem more evident.) In other words, you |
can't post a window which prevents the user from switching |
to another application. You can look at this as a limitation |
of AuntieDialog or you can see it as an opportunity to |
provide a better user experience for your users. We think |
system-modal windows are almost never in the user's best |
interest. And if you do find an exceptional case worthy of a |
system-modal window, it's likely to be very simple, so |
AuntieDialog is probably not called for anyway &emdash; just |
use Dialog Manager for such dialogs.</P> |
<H2>Compatibility</H2> |
<P>You need to query Gestalt to make sure Appearance is |
present before calling AuntieDialog. Consult the DTS Q&A |
"Appearance Versions" for more info, and of course do your |
own testing of any program which incorporates this code. At |
present, AuntieDialog requires CarbonLib, which implies |
Appearance, but AuntieDialog may some day support |
InterfaceLib apps.</P> |
<H2>Data Types</H2> |
<P>There are no new data types in AuntieDialog. Isn't that a |
relief? I've designed this thing to stay out of your way and |
let you use the Window Manager and Control Manager to the |
fullest extent allowed by law. There are some function |
pointer types, but I'm feeling like a lawyer this evening, |
so I'm going to claim these are not data types.</P> |
<H2>Resources</H2> |
<P>I think you'll see a pattern developing here: there are |
no new resource formats in AuntieDialog. Here's how |
AuntieDialog treats the Dialog Manager resources:</P> |
<P><TABLE BORDER=0 CELLSPACING=0 CELLPADDING=10> |
<TR> |
<TD> |
<P ALIGN=center><B>resource type</B></P> |
</TD> |
<TD> |
<P ALIGN=center><B>what is it?</B></P> |
</TD> |
<TD> |
<P><B>AuntieDialog behavior</B></P> |
</TD> |
</TR> |
<TR> |
<TD COLSPAN=3> |
<P> |
<HR> |
</P> |
</TD> |
</TR> |
<TR> |
<TD> |
<P ALIGN=center><CODE>DLOG</CODE></P> |
</TD> |
<TD> |
<P ALIGN=center>dialog template</P> |
</TD> |
<TD> |
<P>fully supported in nearly the same way as Dialog |
Manager</P> |
</TD> |
</TR> |
<TR> |
<TD> |
<P ALIGN=center><CODE>DITL</CODE></P> |
</TD> |
<TD> |
<P ALIGN=center>dialog item list</P> |
</TD> |
<TD> |
<P>fully supported in nearly the same way as Dialog |
Manager</P> |
</TD> |
</TR> |
<TR> |
<TD> |
<P ALIGN=center><CODE>CNTL</CODE></P> |
</TD> |
<TD> |
<P ALIGN=center>control template</P> |
</TD> |
<TD> |
<P>fully supported in nearly the same way as Dialog |
Manager</P> |
</TD> |
</TR> |
<TR> |
<TD> |
<P ALIGN=center><CODE>dftb</CODE></P> |
</TD> |
<TD> |
<P ALIGN=center>new-style<BR> |
dialog item color and text style table</P> |
</TD> |
<TD> |
<P>fully supported in nearly the same way as Dialog |
Manager</P> |
</TD> |
</TR> |
<TR> |
<TD COLSPAN=3> |
<P> |
<HR> |
</P> |
</TD> |
</TR> |
<TR> |
<TD> |
<P ALIGN=center><CODE>ictb</CODE></P> |
</TD> |
<TD> |
<P ALIGN=center>old-style<BR> |
dialog item color and text style table</P> |
</TD> |
<TD> |
<P>ignored in favor of <CODE>dftb</CODE> to |
maximize Appearnce-savviness and minimize |
implementation complexity</P> |
</TD> |
</TR> |
<TR> |
<TD> |
<P ALIGN=center><CODE>dctb</CODE></P> |
</TD> |
<TD> |
<P ALIGN=center>dialog color table</P> |
</TD> |
<TD> |
<P>ignored in favor of maximizing |
Appearance-savviness</P> |
</TD> |
</TR> |
<TR> |
<TD> |
<P ALIGN=center><CODE>dlgx</CODE></P> |
</TD> |
<TD> |
<P ALIGN=center>dialog template extension</P> |
</TD> |
<TD> |
<P>ignored in favor of maximizing |
Appearance-savviness</P> |
</TD> |
</TR> |
<TR> |
<TD> |
<P ALIGN=center><CODE>ALRT</CODE></P> |
</TD> |
<TD> |
<P ALIGN=center>alert template</P> |
</TD> |
<TD> |
<P>not supported; AuntieDialog does not make the |
distinction between alerts and dialogs</P> |
</TD> |
</TR> |
<TR> |
<TD> |
<P ALIGN=center><CODE>alrx</CODE></P> |
</TD> |
<TD> |
<P ALIGN=center>alert template extension</P> |
</TD> |
<TD> |
<P>not supported; AuntieDialog does not make the |
distinction between alerts and dialogs</P> |
</TD> |
</TR> |
<TR> |
<TD> |
<P ALIGN=center><CODE>actb</CODE></P> |
</TD> |
<TD> |
<P ALIGN=center>alert item color and text style |
table</P> |
</TD> |
<TD> |
<P>not supported; AuntieDialog does not make the |
distinction between alerts and dialogs</P> |
</TD> |
</TR> |
</TABLE> |
<BR> |
</P> |
<P>One other thing regarding resources bears repeating and |
clarification: the policy of the HIT with respect to |
releasing resources has always been that all resources are |
assumed purgeable and are never released (though it's safe |
for an HIT client to explicitly release resources as soon as |
that client is certain HIT no longer needs those resources). |
Although we can make some pretty good guesses at the |
reasoning behind this policy, it's difficult to explain |
conclusively because there's not a lot of documentation |
about the smaller design decisions made during the |
development of the original Macintosh. In any case, |
AuntieDialog adopts the same policy in order to avoid |
creating any additional confusion during your debugging |
sessions.</P> |
<H2>AuntieDialog and Control IDs</H2> |
<P>Understanding how AuntieDialog uses control IDs is |
critical. This usage is designed to be as unobtrusive as |
possible while at the same time enabling you to refer to |
controls with index values like you would refer to dialog |
items. If you would like to use other values, you need to be |
aware of the facilities AuntieDialog provides so you can |
make an informed decision as to how to choose those |
values.</P> |
<H3>The Default Behavior and the Values it Produces</H3> |
<P>When AuntieDialog appends a list of controls to a window, |
it assigns each control an ID immediately after creating the |
control. The signature of the ID is kAuntieDialogSignature |
('aunt', a value in the Apple-only range).</P> |
<P>To choose 'id' field of the ID, AuntieDialog counts the |
controls which are already in the window (with some |
exceptions we'll get to in a moment). The first new control |
gets the count + 1, the second control gets the count + 2, |
and so on. So, when you add controls to a window which has |
none, the first control will have ID 1, the second will have |
ID 2, and so on. If you add controls to a window which |
already has three controls, the first control will have ID |
4, the second 5, and so on.</P> |
<P>Any control whose ID signature is not |
kAuntieDialogSignature does not contribute to the count of |
controls in a window. You might wonder where such controls |
would come from, since AuntieDialog always sets the ID |
signature of the controls it creates to |
kAuntieDialogSignature. First, the root control does not |
appear in any dialog item list, is created by AuntieDialog |
automagically, but is not given kAuntieDialogSignature. |
Second, controls which are created by other controls, for |
example the scroll bars created by a list box control, have |
no ID signature. Since for obscure reasons AuntieDialog |
cannot predict where in the control hierarchy these controls |
will appear, it makes no attempt to assign them ID |
signatures, which is probably what you want anyway.</P> |
<P><TABLE BORDER=1 BGCOLOR="#FFFFCC" CELLSPACING=0 CELLPADDING=10> |
<TR> |
<TD> |
<P><B>Note:</B> You can expect standard system |
controls created by other controls to have no ID |
signature, but you may encounter custom controls |
which have other behavior. You may need to set the |
ID signature of these controls to 0 yourself. If it |
isn't possible to change the ID signature for such |
a control, that control is probably not compatible |
with AuntieDialog.</P> |
</TD> |
</TR> |
</TABLE> |
<BR> |
</P> |
<P>AuntieDialog's default control ID is exactly the behavior |
you want if you want ID values which correspond to dialog |
item index values. With a window full of controls whose IDs |
conform to the default behavior of AuntieDialog, you can use |
the function <CODE>GetControlByID</CODE> to get a |
<CODE>ControlRef</CODE> in much the same manner as you would |
use <CODE>GetDialogItemAsControl</CODE>.</P> |
<P>But you must accept one more restriction in order to make |
this work: If you plan to dispose some controls and append |
others &emdash; say, when the user clicks a non-current |
tab in a tabs control &emdash; you should only dispose |
controls whose IDs are at the end of the range of values for |
controls in a given window. This ensures the controls you |
later append will acquire a range of IDs which is contiguous |
with the controls which remain after the deletion. The |
Dialog Manager doesn't allow you to delete items from the |
middle of the list, but Control Manager gives you the |
freedom to dispose any control at any time, so you need to |
be aware of the effect it will have on AuntieDialog. |
Deleting controls whose ID is not at the end of the range |
for a given window creates the possibility of duplicate IDs, |
which would make using <CODE>GetControlByReference</CODE> |
somewhat less than enjoyable. You need to delete controls in |
much the same way as you would shorten a dialog item list |
with ShortenDITL.</P> |
<P><TABLE BORDER=1 BGCOLOR="#FFFFCC" CELLSPACING=0 CELLPADDING=10> |
<TR> |
<TD> |
<P><B>Sidebar:</B> There are some very credible |
people, one of whom owns the Control Manager as of |
this writing, who believe deleting and recreating |
controls is an inherently risky endeavor. One |
problem scenario is when a user clicks a |
non-current tab in a tabs control under adverse |
conditions such as low memory. If the controls for |
the tab which is becoming current cannot be |
created, the operation will fail; the obvious |
fall-back position is to recreate the controls for |
the original tab. Unfortunately, this operation may |
also fail. Now the fallback position is to close |
the window and apologize to the user. However, at |
this point we've involved a lot of code to handle |
exceptional cases, and as most experienced software |
engineers know, this kind of code tends to get the |
least testing and is most likely to blow up. The |
people who concern themselves with this issue think |
it's much better to create all the controls you'll |
ever need as you bring up a window and hide and |
show them as needed. Nothing in AuntieDialog |
prevents or discourages you from using this |
technique. And from the system's perspective, this |
is a non-issue; the system doesn't care why you are |
deleting and creating controls; the responsibility |
for pondering this matter falls squarely into your |
lap.</P> |
</TD> |
</TR> |
</TABLE> |
</P> |
<H3>How to Override the Default Behavior</H3> |
<P>OK, the default behavior is all well and good, but |
suppose you're ready to go beyond simulated dialog item list |
indexes. If you want control IDs to follow some other logic |
(say, pointers to instances of a C++ class), you should |
probably specify a non-NIL control creation function:</P> |
<PRE>typedef pascal |
OSStatus (*ControlCreationProcPtr) (ControlRef);</PRE> |
<P>You write a function of the above type, and it is called |
from inside <CODE>AppendDialogItemsAsControls</CODE> after |
each control is created. (The control's ID has already been |
assigned according to the default behavior described above.) |
Your function decides whether and how to change the control, |
including disposing it, based on the control itself and |
whatever other criteria you choose.</P> |
<P>You can, of course, change the ID of any control (using |
<CODE>SetControlID</CODE>), including those created by |
AuntieDialog, but you will probably want to make sure the |
value is unique with respect to a given window if you want |
<CODE>GetControlByID</CODE> to continue working well.</P> |
<P>Note that addresses of heap blocks (including those |
produced by <CODE>NewPtr</CODE>, <CODE>NewHandle</CODE>, |
<CODE>malloc</CODE>, and <CODE>operator new</CODE>) are |
unique even beyond the context of a given window, so if you |
want to associate the address of a heap block (say, a C++ |
object pointer) with a control, that address will be |
suitable for use as a search key for |
<CODE>GetControlByID</CODE>. Be sure, though, that all |
controls in a given window have IDs which are unique with |
respect to each other. For example, don't expect a C++ |
object pointer to be a useful control ID if the rest of the |
control IDs for the controls in the given window are still |
dialog item list indexes. (Not that a C++ object pointer |
would ever be as low as a dialog item list index, but you |
should still be careful.)</P> |
<P>This callback function should not allow C++ exceptions to |
propagate.</P> |
<H2>Functions</H2> |
<P>The AuntieDialog package contains functions for creating |
a window full of controls, for appending lists of controls, |
for removing child controls, for counting the controls in a |
window, and for handling events in such a window. The rest |
is up to you and Window Manager and Control Manager.</P> |
<H3>CountControlsInWindow</H3> |
<PRE>OSStatus CountControlsInWindow (WindowRef, UInt16 *);<BR></PRE> |
<P>This function is roughly analagous to |
<CODE>CountDITL</CODE>. It counts the number of controls in |
a window, excluding the root control and any controls whose |
ID signature is not kAuntieDialogSignature. If the window |
has no root control, this function fails with an error. |
Controls with ID signatures other than |
kAuntieDialogSignature are skipped under the assumption that |
they are created by other controls, such as the scroll bar |
controls created by the list box control, and not by |
AuntieDialog. (Keep this is mind if you assign your own ID |
values.) You may find this function useful if you adopt the |
AuntieDialog convention of storing dialog item list indexes |
as control ID values. If you need to know the ID of the |
first control which would be added by calling |
<CODE>AppendDialogItemsAsControls</CODE>, call |
<CODE>CountControlsInWindow</CODE> and add one to the |
result.</P> |
<H3>AppendDialogItemsAsControls</H3> |
<PRE>OSStatus AppendDialogItemsAsControls |
(short resID, WindowRef, ControlCreationProcPtr);<BR></PRE> |
<P>This function is roughly analgous to |
<CODE>AppendDialogItemList</CODE>. The chief difference, of |
course, is that this function creates controls, not dialog |
items. Another important difference is that this function |
makes no attempt to place the new controls anywhere in |
particular within the window or resize the window to fit the |
new controls. The dialog item list template whose ID you |
specify contains the final location of the controls, and |
they must fit in the window if you want them to be visible |
to the user.</P> |
<P><CODE>AppendDialogItemsAsControls</CODE> is called by |
<CODE>NewAuntieDialog</CODE>.</P> |
<P><CODE>AppendDialogItemsAsControls</CODE> inhibits control |
drawing and invalidates control regions if the parent window |
is visible to prevent some controls from drawing before |
others. So, if you have a window for which you must call |
<CODE>GetAuntieDialog</CODE> and then |
<CODE>AppendDialogItemsAsControls</CODE> before allowing the |
user to interact with the window, you won't have to work |
around the bizarre non-linear drawing behavior you would |
have gotten from Dialog Manager.</P> |
<P><CODE>AppendDialogItemsAsControls</CODE> auto-embeds |
controls just as Dialog Manager does.</P> |
<P>Unlike Dialog Manager, when |
<CODE>AppendDialogItemsAsControls</CODE> creates user pane |
controls based on dialog user items, the user pane controls |
support embedding, so if you need a user pane control which |
doesn't support embedding, you'll need to create an |
appropriate control resource.</P> |
<P>If you pass a NIL reference provider, |
<CODE>AppendDialogItemsAsControls</CODE> will append |
controls whose IDs have been assigned in the default |
manner.</P> |
<H3>NewAuntieDialog</H3> |
<PRE>OSStatus NewAuntieDialog |
(short resID, WindowRef window, ControlCreationProcPtr);<BR></PRE> |
<P>This function is roughly analgous to |
<CODE>NewDialog</CODE>. <CODE>NewAuntieDialog</CODE> calls |
<CODE>AppendDialogItemsAsControls</CODE> and is called by |
<CODE>GetAuntieDialog</CODE>. The resource ID specifies the |
dialog item list resource which is to be used as a template. |
This function allows you to create your own window, perhaps |
with an API such as <CODE>CreateNewWindow</CODE>, and pass |
it to the AuntieDialog package to be populated with |
controls. This function will fail if the window already |
contains controls.</P> |
<H3>GetAuntieDialog</H3> |
<PRE><CODE>OSStatus GetAuntieDialog |
(short resID, WindowRef behind, |
ControlCreationProcPtr, WindowRef *result);<BR></CODE></PRE> |
<P><CODE>GetAuntieDialog</CODE> is roughly analagous to |
<CODE>GetNewDialog</CODE>. <CODE>resID</CODE> is the |
resource ID of the dialog resource from which you want to |
build a window. <CODE>behind</CODE> is the window behind |
which you wish the new window to appear. And |
<CODE>result</CODE> is where the reference to the newly |
created window will be put.</P> |
<P><CODE>GetAuntieDialog</CODE> produces a |
<CODE>WindowRef</CODE>, not a <CODE>DialogRef</CODE>. Don't |
expect the window that's produced to be recognized by the |
Dialog Manager. It's not a dialog. That would defeat the |
purpose of AuntieDialog.</P> |
<P><CODE>GetAuntieDialog</CODE> always creates a color |
window and ignores any dialog color table resource in favor |
of the colors and patterns in the theme the user has chosen. |
<CODE>GetAuntieDialog</CODE> also ignores any dialog |
extension resource and creates the window as if such a |
resource with all its bits set is present, which means you |
always get a theme background and you always get theme |
controls. (The remaining bits are handled elsewhere in |
AuntieDialog or by your program.)</P> |
<P><CODE>GetAuntieDialog</CODE> ignores any extra bits on |
the end of the dialog template resource which the Dialog |
Manager would have used to align the window. The support for |
these bits has never been well-defined or bug-free, and |
<CODE>RepositionWindow</CODE>, an API introduced in Mac OS |
8.5, is so vastly superior to the old alignment support that |
I decided it was better to leave the issue unaddressed.</P> |
<P><CODE>GetAuntieDialog</CODE> always creates the window |
initially invisible and only shows it (if the dialog |
resource specifies the window is to be shown) after creating |
all the controls. This minimizes flicker.</P> |
<H3>DisposeChildControls</H3> |
<PRE>OSStatus DisposeChildControls (ControlRef);<BR></PRE> |
<P>This function disposes the child controls of the given |
parent control. This will be useful mostly in conjunction |
with <CODE>AppendDialogItemsAsControls</CODE>; you may wish |
to delete some controls and append some others, for example |
when the user clicks a new tab in a tab control you're using |
as a parent control.</P> |
<P>This function differs from <CODE>DisposeControl</CODE> in |
that <CODE>DisposeControl</CODE> would dispose the parent in |
addition to the children.</P> |
<P>This function differs from <CODE>ShortenDITL</CODE> not |
only in that it operates on controls and not dialog items |
but also in that it removes child controls regardless of |
where they appeared in the dialog item list before they |
became controls. This may create holes in the range of |
control IDs. You may want to make sure that the child |
controls you dispose appear at the end of the dialog item |
list they came from. That way, when you append other |
controls, their IDs will be in sequence with the rest of the |
controls in the window.</P> |
<H2> |
<HR> |
<BR> |
To do:</H2> |
<UL> |
<LI>Add more application-level logic to MegaDialog |
simulation.<BR> |
<BR> |
</LI> |
<LI>Describe all the functions, including the |
callbacks.<BR> |
<BR> |
</LI> |
<LI>Passing the root control to DeactivateControl to |
propagate latent grayness doesn't always work properly on |
older systems. Should we work around this?<BR> |
<BR> |
</LI> |
<LI>AuntieDialogGetIdleInterval should traverse every |
control in every AuntieDialog window to determine which |
control would request the shortest idle if it could.<BR> |
<BR> |
</LI> |
<LI>Finish the compatibility hacks in |
ShouldDrawControlsOffScreen.<BR> |
<BR> |
</LI> |
<LI>Work around Radar 2459917 (not enough mouse-moved |
events)</LI> |
</UL> |
</TD> |
</TR> |
</TABLE> |
</P> |
</BODY> |
</HTML> |
Copyright © 2003 Apple Computer, Inc. All Rights Reserved. Terms of Use | Privacy Policy | Updated: 2003-01-30