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.
MIB-Libraries/MoreControls/MoreControls.cp
/* |
File: MoreControls.cp |
Contains: Control Manager utilities. |
Written by: Pete Gontier |
Copyright: Copyright (c) 1998-2001 by Apple Computer, Inc., All Rights Reserved. |
Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple Computer, Inc. |
("Apple") in consideration of your agreement to the following terms, and your |
use, installation, modification or redistribution of this Apple software |
constitutes acceptance of these terms. If you do not agree with these terms, |
please do not use, install, modify or redistribute this Apple software. |
In consideration of your agreement to abide by the following terms, and subject |
to these terms, Apple grants you a personal, non-exclusive license, under AppleÕs |
copyrights in this original Apple software (the "Apple Software"), to use, |
reproduce, modify and redistribute the Apple Software, with or without |
modifications, in source and/or binary forms; provided that if you redistribute |
the Apple Software in its entirety and without modifications, you must retain |
this notice and the following text and disclaimers in all such redistributions of |
the Apple Software. Neither the name, trademarks, service marks or logos of |
Apple Computer, Inc. may be used to endorse or promote products derived from the |
Apple Software without specific prior written permission from Apple. Except as |
expressly stated in this notice, no other rights or licenses, express or implied, |
are granted by Apple herein, including but not limited to any patent rights that |
may be infringed by your derivative works or by other works in which the Apple |
Software may be incorporated. |
The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES NO |
WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED |
WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN |
COMBINATION WITH YOUR PRODUCTS. |
IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR |
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE |
GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION |
OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF CONTRACT, TORT |
(INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN |
ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
Change History (most recent first): |
$Log: MoreControls.cp,v $ |
Revision 1.12 2003/04/14 15:50:09 |
In CopyTextControlTextCompat, use NewPtr rather than malloc to prevent "malloc(0) returns NULL" problem. |
Revision 1.11 2002/11/25 18:26:46 |
Copy/SetTextControlTextCompat have to take a password parameter because you have to do different actions based on whether the control is a password control on traditional Mac OS, and there's no way to determine that from the control. |
Revision 1.10 2002/11/08 23:18:13 |
When using framework includes, explicitly include the frameworks we need. Added GetControlByIDQ helper. Added routines to get/set text control text as a CFString. Added key and value filters for edit text controls; these allow you to get a Carbon event when an edit text control is modified. Added SetControlActive/Visible helpers. Include our prototype early to flush out any missing dependencies. Convert MoreAssert to MoreAssertPCG. Convert MoreAssertQ to assert. Eliminate implicit arithmetic conversion from CW. Convert nil to NULL. |
Revision 1.9 2001/11/07 15:51:59 |
Tidy up headers, add CVS logs, update copyright. |
<8> 21/9/01 Quinn Changes for CWPro7 Mach-O build. |
<7> 15/2/01 Quinn Minor tweaks to get it building under Project Builder. |
<6> 1/3/00 Quinn Completely rewrite GetControlDefProcResID. This is the best |
approximation of this routine possible prior to new features |
that should be introduced in CarbonLib 1.1. It wonÕt work |
properly on Mac OS X. Also some other minor Carbon changes. |
<5> 2/23/99 PCG fix copy and paste bug in MoveControlNear introduced during |
Carbonation |
<4> 2/9/99 PCG more TARGET_CARBON |
<3> 1/25/99 PCG TARGET_CARBON |
<2> 11/11/98 PCG fix header |
<1> 11/10/98 PCG first big re-org at behest of Quinn |
Old Change History (most recent first): |
<5> 10/29/98 PCG various |
<4> 9/1/98 PCG Universal Headers 3.2 |
<3> 7/24/98 PCG eliminate dependency on 'qd' |
<2> 7/21/98 PCG remove annoying assert in Get1NewStaticTextControl |
<2> 6/23/98 PCG add IsScrollBar |
<1> 6/16/98 PCG initial checkin |
*/ |
#include "MoreControls.h" |
#include "MoreAppearance.h" |
#include "MoreWindows.h" |
#include "MoreQuickDraw.h" |
#include "MoreMemory.h" |
#if TARGET_API_MAC_CARBON |
#include "MoreCFQ.h" |
#endif |
#if ! MORE_FRAMEWORK_INCLUDES |
#include <Script.h> |
#include <MacWindows.h> |
#include <Resources.h> |
#include <Gestalt.h> |
#endif |
#include <stdlib.h> |
typedef struct |
{ |
// |
// Used by AppendTabInfo and its callers. |
// Caches tab contents data temporarily. |
// |
Boolean enabled; |
ControlTabInfoRec ctir; |
} |
FullControlTabInfoRec, *FullControlTabInfoRecP, **FullControlTabInfoRecH; |
// |
// gControlScratchStr is used to relieve us from having to declare |
// a Str255 on the stack when we call GetResInfo and we don't care |
// about the name of the resource. |
// |
static Str255 gControlScratchStr; |
// |
// AssertControlHasDefProcResID is a macro which asserts that |
// a given control's control definition procedure resource has |
// a given resource ID. This is generally used to validate |
// assumptions about the data tags supported by a given |
// control definition procedure. |
// |
#if MORE_DEBUG |
static pascal Boolean MoreAssertControlHasDefProcResID |
(ControlHandle c, short requiredDPR) |
{ |
SInt16 actualDPR; |
if (!MoreAssertPCG (!GetControlDefProcResID (c,&actualDPR))) return false; |
if (!MoreAssertPCG (actualDPR == requiredDPR)) return false; |
return true; |
} |
#else |
# define MoreAssertControlHasDefProcResID(c,requiredDPR) (true) |
#endif |
#if PRAGMA_STRUCT_ALIGN |
#pragma options align=mac68k |
#elif PRAGMA_STRUCT_PACKPUSH |
#pragma pack(push, 2) |
#elif PRAGMA_STRUCT_PACK |
#pragma pack(2) |
#endif |
struct HackControlRecord { |
ControlRef nextControl; |
WindowRef contrlOwner; |
Rect contrlRect; |
UInt8 contrlVis; |
UInt8 contrlHilite; |
SInt16 contrlValue; |
SInt16 contrlMin; |
SInt16 contrlMax; |
Handle contrlDefProc; |
}; |
typedef struct HackControlRecord HackControlRecord, *HackControlRecordPtr, **HackControlRecordHandle; |
#if PRAGMA_STRUCT_ALIGN |
#pragma options align=reset |
#elif PRAGMA_STRUCT_PACKPUSH |
#pragma pack(pop) |
#elif PRAGMA_STRUCT_PACK |
#pragma pack() |
#endif |
pascal OSStatus GetControlDefProcResID (ControlHandle control, short *resID) |
{ |
// |
// Given a control, determines what the resource ID of the control's |
// control definition procedure resource. If the resource has been |
// detached, this routine will fail and return an appropriate error. |
// Don't detach system definition procedure resources! Other people |
// may be using them as resources (expecting LoadResource to work, etc.) |
// |
OSStatus err; |
ResType junkResType; |
UInt32 gestaltResponse; |
Handle controlDefH; |
err = noErr; |
// In Carbon 1.1 and higher (hopefully), there will be an API for doing this |
// properly. The current plan is that this API will be called GetControlKind. |
// Unfortunately, this API is not in any current headers or libraries, so |
// I canÕt conditionally use it. Instead, IÕve just put in a hack implementation. |
// When that API is available, this routine will need to be modified to take |
// advantage of it. Until then, software that absolutely relies on this |
// routine will not work properly on Mac OS X. This shouldnÕt be a problem |
// because Mac OS X will ship with Carbon 1.1. |
if ( (Gestalt(gestaltCarbonVersion, (SInt32 *) &gestaltResponse) == noErr) |
&& (gestaltResponse >= 0x011)) { |
#if MORE_DEBUG |
DebugStr("\pGetControlDefProcResID: Should be using GetControlKind"); |
#endif |
} |
// The technique IÕm currently using wonÕt work on Mac OS X, so we return |
// an error in that case. |
if ( (Gestalt(gestaltSystemVersion, (SInt32 *) &gestaltResponse) == noErr) |
&& (gestaltResponse >= 0x0a0)) { |
err = unimpErr; |
} |
// Now use the old, traditional Mac OS-only method of grabbing the control |
// definition handle out of the control handle. |
if (err == noErr) { |
controlDefH = (**((HackControlRecordHandle) control)).contrlDefProc; |
GetResInfo(controlDefH, resID, &junkResType, gControlScratchStr); |
err = ResError(); |
assert(err || (junkResType == kControlDefProcType)); |
} |
return err; |
} |
static pascal MoreControls_tAlignment FilterAlignment (MoreControls_tAlignment align) |
{ |
// |
// If this function is passed MoreControls_kAlignSystem, it determines |
// what the correct alignment constant is based on the direction of the |
// system script. This way radio buttons on an Arabic system, whose |
// bubbles are on the right side, will align down the right edge without |
// the client program needing to know the details. |
// |
if (align == MoreControls_kAlignSystem) |
align = GetSysDirection ( ) == -1 ? MoreControls_kAlignRight : MoreControls_kAlignLeft; |
assert(align == MoreControls_kAlignLeft || |
align == MoreControls_kAlignCenter || align == MoreControls_kAlignRight); |
return align; |
} |
#pragma mark - |
#if 0 |
// Pete Gontier left this routine in this file, but itÕs neither exported |
// not used by the other routines in the file. Rather than delete it, |
// IÕve just left it here in case we ever need to resurrect it. |
// -- Quinn, 8 Feb 2001 |
static pascal OSStatus AppendTabInfo |
(ControlHandle whichControl, UInt16 tabIndex, |
FullControlTabInfoRecP tabInfoP, FullControlTabInfoRecH tabInfoPackedArrayH) |
{ |
OSStatus err = noErr; |
// |
// For a given tab, this function gets the tab icon and string as |
// well whether the tab is enabled. Appends said data onto the end |
// of an array stored in a handle. |
// |
// This is a helper function. tabInfoP is supposed to have been allocated |
// by the caller, but the caller is not expected to care what's in it; |
// we do this so the caller can call AppendTabInfo in a loop without |
// this helper function repeatedly creating and destroying the buffer, |
// which would be slow, we presume. It's a classic case of optimizing without |
// first measuring. |
// |
if (!(MoreAssertPCG (tabIndex && tabIndex <= GetControlMaximum (whichControl)))) |
err = paramErr; |
else |
{ |
Size actualSize; |
tabInfoP->ctir.version = 0; |
if (!(err = GetControlData (whichControl, tabIndex, kControlTabInfoTag, |
sizeof (tabInfoP->ctir), Ptr (&(tabInfoP->ctir)), &actualSize))) |
{ |
if (!(err = GetControlData (whichControl, tabIndex, kControlTabEnabledFlagTag, |
sizeof (tabInfoP->enabled), Ptr (&(tabInfoP->enabled)), &actualSize))) |
{ |
if (!MoreAssertPCG (actualSize == sizeof (tabInfoP->enabled))) |
err = paramErr; |
else |
err = PtrAndHand (tabInfoP, Handle (tabInfoPackedArrayH), 1 + *(tabInfoP->ctir.name) + (sizeof (*tabInfoP) - sizeof (Str255))); |
} |
} |
} |
return err; |
} |
#endif |
pascal OSStatus MoreGetControlRegion (ControlHandle control, RgnHandle *rgn) |
{ |
// |
// Given a control, allocates and initializes a region describing the |
// control's bounds. Note this is different from (**control).contrlRect; |
// a control whose control definition function calls DrawThemeEditTextFrame |
// will (should) report its region as being 2 pixels larger than its contrlRect. |
// |
OSStatus err = noErr; |
if (!MoreAssertPCG (HaveAppearance ( ))) |
err = paramErr; |
else if (!MoreAssertPCG (control && rgn)) |
err = nilHandleErr; |
else |
{ |
*rgn = NewRgn ( ); |
if (!(err = QDError ( ))) |
{ |
// if (GetControlRegion) |
// err = GetControlRegion (control,whichPartCodeIsEquivalent,*rgn); |
// else |
#if UNIVERSAL_INTERFACES_VERSION <= 0x0332 |
(void) SendControlMessage (control, calcCntlRgn, (SInt32) *rgn); |
#else |
(void) SendControlMessage (control, calcCntlRgn, *rgn); |
#endif |
if (err) |
{ |
DisposeRgn (*rgn); |
assert(noErr == QDError ( )); |
*rgn = NULL; |
} |
} |
if (err) |
*rgn = NULL; |
} |
return err; |
} |
pascal OSStatus InvalControl (ControlHandle control) |
{ |
OSStatus err = noErr; |
// |
// Given a control, invalidates the area occupied by the control. |
// |
if (!(MoreAssertPCG (control))) |
err = nilHandleErr; |
else |
{ |
RgnHandle rgn; |
err = MoreGetControlRegion (control, &rgn); |
if (!err) |
{ |
GrafPtr preservePort; |
GetPort (&preservePort); |
WindowRef controlOwner = GetControlOwner (control); |
SetPortWindowPort (controlOwner); |
InvalWindowRgn (controlOwner,rgn); |
SetPort (preservePort); |
DisposeRgn (rgn); |
} |
} |
return err; |
} |
pascal OSStatus ValidControl (ControlHandle control) |
{ |
OSStatus err = noErr; |
// |
// Given a control, validates the area occupied by the control. |
// |
if (!(MoreAssertPCG (control))) |
err = nilHandleErr; |
else |
{ |
RgnHandle rgn; |
err = MoreGetControlRegion (control, &rgn); |
if (!err) |
{ |
GrafPtr preservePort; |
GetPort (&preservePort); |
WindowRef controlOwner = GetControlOwner (control); |
SetPortWindowPort (controlOwner); |
ValidWindowRgn (controlOwner,rgn); |
SetPort (preservePort); |
DisposeRgn (rgn); |
} |
} |
return err; |
} |
pascal OSStatus SetTabIcon (ControlHandle control, ControlPartCode part, short iconSuiteID) |
{ |
OSStatus err = noErr; |
// |
// This function demonstrates how to set the icon of a tab. |
// Unfortunately, under 1.0.X, the control definition won't |
// listen if the tab already has an icon which has been drawn. |
// We don't assert for this because it works if the tab has |
// no icon. Be careful. |
// |
if (!MoreAssertPCG (HaveAppearance ( ))) |
err = paramErr; |
else if (!MoreAssertPCG (control)) |
err = nilHandleErr; |
else if (!MoreAssertPCG (part >= GetControlMinimum (control))) |
err = paramErr; |
else if (!(MoreAssertPCG (part <= GetControlMaximum (control)))) |
err = paramErr; |
else |
{ |
ControlTabInfoRec *ctirp = (ControlTabInfoRec *) NewPtr (sizeof (*ctirp)); |
if (!ctirp) |
err = MemError ( ); |
else |
{ |
Size actualSize; |
ctirp->version = 0; |
if (!(err = GetControlData (control,part,kControlTabInfoTag,sizeof(ControlTabInfoRec), |
Ptr(ctirp),&actualSize))) |
{ |
ctirp->iconSuiteID = iconSuiteID; |
err = SetControlData (control,part,kControlTabInfoTag,actualSize,Ptr(ctirp)); |
} |
DisposePtr (Ptr (ctirp)); |
assert(noErr == MemError ( )); |
} |
} |
return err; |
} |
pascal OSStatus CopyTabFontStyle (ControlHandle oldTabs, ControlHandle newTabs) |
{ |
// |
// Copies the control font style data from one tab control to another. |
// |
OSStatus err = noErr; |
if (!MoreAssertPCG (HaveAppearance ( ))) |
err = paramErr; |
else if (!MoreAssertPCG (oldTabs && newTabs)) |
err = nilHandleErr; |
else if (!MoreAssertControlHasDefProcResID (oldTabs,kControlTabsDefProcResID)) |
err = paramErr; |
else if (!MoreAssertControlHasDefProcResID (newTabs,kControlTabsDefProcResID)) |
err = paramErr; |
else |
{ |
ControlFontStyleRec cfsr; |
Size actualSize; |
if (!(err = GetControlData (oldTabs,kControlNoPart,kControlTabFontStyleTag,sizeof(cfsr),Ptr(&cfsr),&actualSize))) |
err = SetControlData (newTabs,kControlNoPart,kControlTabFontStyleTag,actualSize,Ptr(&cfsr)); |
} |
return err; |
} |
static pascal OSStatus DupTabsControl_Internal (ControlHandle old, ControlHandle *dup, SInt16 newControlMax) |
{ |
// |
// This is a helper function containing common code to support |
// other exported functions. It creates a control with an |
// appropriate number of tabs, then embeds the control at |
// the same place in the hierarchy as the old control. This |
// function does not copy tab data from the old control to the |
// new control, though it does copy the style info. |
// |
OSStatus err = noErr; |
if (!MoreAssertPCG (HaveAppearance ( ))) |
err = paramErr; |
else if (!MoreAssertPCG (GetAppearanceVersion ( ) >= 0x0101)) |
err = paramErr; |
else if (!MoreAssertPCG (old && dup)) |
err = nilHandleErr; |
else if (!MoreAssertControlHasDefProcResID (old,kControlTabsDefProcResID)) |
err = paramErr; |
else if (!MoreAssertPCG (GetControlMaximum (old) <= newControlMax)) |
err = paramErr; |
// |
// We assert that the old control is visible because there's a bug |
// in the tabs CDEF such that it won't write tab data into a non-visible |
// control. We copy tab data into the new control, so why do we |
// assert the old one's state? Because we are duplicating the old one to |
// the new one, and if the new one must be visible, it's not possible |
// to duplicate an invisible control (the old one) to a visible one |
// (the new one), because that wouldn't be duplication, now, would it? |
// |
// else if (!MoreAssertPCG (IsControlVisible (old))) |
// err = paramErr; |
else |
{ |
ControlVariant variant = GetControlVariant (old); |
WindowPtr oldControlOwner = GetControlOwner (old); |
Rect boundsRect; |
GetControlBounds (old,&boundsRect); |
*dup = NewControl (oldControlOwner, &boundsRect, "\p", IsControlVisible (old), |
0, 1, newControlMax, (short) ((kControlTabsDefProcResID << 4) | variant), 0); |
if (!*dup) |
err = nilHandleErr; |
else |
{ |
if (!(err = CopyTabFontStyle (old,*dup))) |
{ |
// |
// Embed duplicate control in old control's super-control. |
// If the old control is in a window without an embedding |
// hierarchy, do nothing. |
// |
ControlHandle super; |
if (!(err = GetSuperControl (old,&super))) |
err = EmbedControl (*dup,super); |
else if (err == errNoRootControl) |
err = noErr; |
// |
// Make sure the old and new controls have the same tab selected. |
// |
if (!err) |
SetControlValue (*dup, GetControlValue (old)); |
} |
if (err) |
{ |
DisposeControl (*dup); |
*dup = NULL; |
} |
} |
} |
return err; |
} |
static pascal OSStatus CopyTab |
(ControlHandle sourceControl, ControlHandle destControl, SInt16 sourceTab, SInt16 destTab) |
{ |
// |
// Copies the tab data from a single tab in one control to a single tab in another control. |
// This is a helper function which is designed to be called from inside a loop. |
// |
OSStatus err = noErr; |
if (!MoreAssertPCG (HaveAppearance ( ))) |
err = paramErr; |
else if (!MoreAssertPCG (GetAppearanceVersion ( ) >= 0x0101)) |
err = paramErr; |
else if (!MoreAssertPCG (sourceControl)) |
err = nilHandleErr; |
else if (!MoreAssertPCG (destControl)) |
err = nilHandleErr; |
else if (!MoreAssertControlHasDefProcResID (sourceControl,kControlTabsDefProcResID)) |
err = paramErr; |
else if (!MoreAssertControlHasDefProcResID (destControl,kControlTabsDefProcResID)) |
err = paramErr; |
else if (!MoreAssertPCG (sourceTab <= GetControlMaximum (sourceControl))) |
err = paramErr; |
else if (!MoreAssertPCG (destTab <= GetControlMaximum (destControl))) |
err = paramErr; |
else |
{ |
Size actualSize; |
ControlTabInfoRec *ctirp = (ControlTabInfoRec *) NewPtr (sizeof (*ctirp)); |
if (!ctirp) |
err = MemError ( ); |
else |
{ |
Boolean tabIsEnabled; |
ctirp->version = 0; |
do |
{ |
err = GetControlData (sourceControl,sourceTab,kControlTabInfoTag,sizeof(*ctirp),Ptr(ctirp),&actualSize); |
if (err) break; |
err = SetControlData (destControl,destTab,kControlTabInfoTag,actualSize,Ptr(ctirp)); |
if (err) break; |
err = GetControlData (sourceControl,sourceTab,kControlTabEnabledFlagTag,sizeof(tabIsEnabled),Ptr(&tabIsEnabled),&actualSize); |
if (err) break; |
err = SetControlData (destControl,destTab,kControlTabEnabledFlagTag,actualSize,Ptr(&tabIsEnabled)); |
if (err) break; |
} |
while (false); |
DisposePtr (Ptr (ctirp)); |
assert(noErr == MemError ( )); |
} |
} |
return err; |
} |
pascal OSStatus InsertTab (ControlHandle oldTabs, ControlHandle *dup, SInt16 after) |
{ |
// |
// Adds a single tab to a tabs control. Since there are no APIs for adding |
// a tab (except at the end of the list [and that feature is buggy anyway]), |
// we duplicate the entire control with an additional tab, then copy the tab |
// data from the old control to the new control, leaving a gap for the new tab. |
// The after parameter, of course, specifies the tab after which the new tab |
// should appear. 0 is a valid value, even though it does not correspond to |
// any existing tab; it means the new tab should appear first. This function |
// also handles unreasonably high values by assuming you want a tab added after |
// the last existing tab. |
// |
OSStatus err = noErr; |
if (!(MoreAssertPCG (oldTabs && dup))) |
err = nilHandleErr; |
else if (!MoreAssertControlHasDefProcResID (oldTabs,kControlTabsDefProcResID)) |
err = paramErr; |
else |
{ |
SInt16 oldMax = GetControlMaximum (oldTabs); |
ControlHandle newTabs; |
if (!(err = DupTabsControl_Internal (oldTabs, &newTabs, (short) (oldMax + 1)))) |
{ |
if (SInt16 index = oldMax) |
{ |
if (after > index) after = index; |
if (!(MoreAssertPCG (index >= after))) |
err = paramErr; |
else |
{ |
// copy the tab data for the tabs after the new one |
while (index > after) |
{ |
err = CopyTab (oldTabs,newTabs,index,(short)(index+1)); |
if (err) break; |
--index; |
} |
// copy the tab data for the tabs before the new one |
if (!err) while (index) |
{ |
err = CopyTab (oldTabs,newTabs,index,index); |
if (err) break; |
--index; |
} |
} |
} |
if (err) |
DisposeControl (newTabs); |
else |
{ |
short oldValue = GetControlValue (oldTabs); |
if (oldValue > after) |
SetControlValue (newTabs, (short) (oldValue + 1)); |
*dup = newTabs; |
} |
} |
} |
return err; |
} |
pascal OSStatus RemoveTab (ControlHandle oldTabs, ControlHandle *dup, SInt16 victim) |
{ |
// |
// Removes a single tab from a tabs control. Since there are no APIs for |
// removing an arbitrary tab except at the end of the list, we duplicate |
// the entire control with one fewer tab, then copy the tab data from the |
// old control to the new control, skipping the doomed tab. |
// |
OSStatus err = noErr; |
if (!MoreAssertPCG (victim && oldTabs && dup)) |
err = nilHandleErr; |
else if (!MoreAssertControlHasDefProcResID (oldTabs,kControlTabsDefProcResID)) |
err = paramErr; |
else |
{ |
SInt16 max = GetControlMaximum (oldTabs); |
if (!MoreAssertPCG (max && max >= victim)) |
err = paramErr; |
else |
{ |
ControlHandle newTabs; |
if (!(err = DupTabsControl_Internal (oldTabs, &newTabs, max))) |
{ |
SInt16 index = 1; |
// copy the tab data for the tabs before the victim |
while (index < victim) |
{ |
err = CopyTab (oldTabs,newTabs,index,index); |
if (err) break; |
++index; |
} |
// copy the tab data for the tabs after the victim |
if (!err) while (index <= max - 1) |
{ |
err = CopyTab (oldTabs, newTabs, (short) (index + 1), index); |
if (err) break; |
++index; |
} |
if (err) |
DisposeControl (newTabs); |
else |
{ |
SetControlMaximum (newTabs, (short) (max - 1)); |
short oldValue = GetControlValue (oldTabs); |
if (oldValue > victim) |
{ |
short newValue = (short) (oldValue - 1); |
if (newValue != GetControlValue (newTabs)) |
SetControlValue (newTabs, newValue); |
} |
*dup = newTabs; |
} |
} |
} |
} |
return err; |
} |
pascal OSStatus AppendTabs (ControlHandle control, UInt16 count) |
{ |
// |
// The tabs CDEF in Appearance 1.0.X has a fencepost |
// error in which it trashes its private tab data when |
// growing the tabs, so we detect this and refuse to |
// allow it to occur. If you need to add tabs to a tabs |
// control under Appearance 1.0.X, you need to use |
// InsertTab. The API is more cumbersome, but it does |
// work. |
// |
OSStatus err = noErr; |
if (!(MoreAssertPCG (control))) |
err = nilHandleErr; |
else if (!MoreAssertControlHasDefProcResID (control,kControlTabsDefProcResID)) |
err = paramErr; |
else if (!MoreAssertPCG (GetAppearanceVersion ( ) > 0x0101)) |
err = paramErr; |
else |
{ |
SInt16 max = GetControlMaximum (control); |
SetControlMaximum (control, (SInt16) (max + count)); |
} |
return err; |
} |
pascal OSStatus TruncateTabs (ControlHandle control, UInt16 count) |
{ |
// |
// The tabs CDEF in Appearance 1.0.X has a fencepost |
// error in which it trashes its private tab data when |
// shrinking the tabs, so we detect this and refuse to |
// allow it to occur. If you need to truncate tabs from |
// a tabs control under Appearance 1.0.X, you need to use |
// RemoveTab. The API is more cumbersome, but it does |
// work. |
// |
OSStatus err = noErr; |
if (!(MoreAssertPCG (control))) |
err = nilHandleErr; |
else if (!MoreAssertControlHasDefProcResID (control,kControlTabsDefProcResID)) |
err = paramErr; |
else if (!MoreAssertPCG (GetAppearanceVersion ( ) > 0x0101)) |
err = paramErr; |
else |
{ |
SInt16 max = GetControlMaximum (control); |
if (!MoreAssertPCG (max && max >= count)) |
err = paramErr; |
else |
SetControlMaximum (control, (SInt16) (max - count)); |
} |
return err; |
} |
#if ! TARGET_API_MAC_CARBON |
static pascal OSStatus SpoofFindControlUnderMouseForTabs |
(Point where, WindowRef window, ControlPartCode *cpc, ControlHandle whichControl) |
{ |
// |
// The 1.0.X Tabs control has a bug which causes |
// FindControlUnderMouse to produce kControlNoPart when the |
// mouse is over the current tab. The work-around is a quick |
// and dirty hack we can get away with because it only kicks |
// in under certain (old) versions of the API which we know |
// will never change. When this code was written, the bug had |
// been fixed in a pre-release version of Mac OS. |
// There's no iron-clad guarantee that version will ship with |
// the version number I expect it to, so there is some small but |
// non-zero chance this code may fail to work around the bug |
// if it is present in a system with a version of Appearance |
// greater than 1.0.1. |
// |
OSStatus err = noErr; |
if (*cpc == kControlNoPart && GetAppearanceVersion ( ) <= 0x0101) |
{ |
SInt16 contrlValue = GetControlValue (whichControl); |
(**whichControl).contrlValue = 0; |
ControlHandle newWhichControl = FindControlUnderMouse (where,window,cpc); |
assert(newWhichControl == whichControl); |
(**whichControl).contrlValue = contrlValue; |
} |
return err; |
} |
#endif |
static pascal OSStatus SpoofFindControlUnderMouseForPopUpButton (ControlPartCode *cpc) |
{ |
// |
// The popup button control has bugs which cause FindControlUnderMouse |
// to produce kControlNoPart when the mouse is over the title and |
// kControlLabelPart when the mouse is over the menu. |
// |
OSStatus err = noErr; |
if (*cpc == kControlLabelPart) |
*cpc = kControlMenuPart; |
else if (*cpc == kControlNoPart) |
*cpc = kControlLabelPart; |
return err; |
} |
pascal OSStatus MoreFindControlUnderMouse |
(Point where, WindowRef window, ControlPartCode *cpc, ControlHandle *whichControl) |
{ |
// |
// Second-guesses the results of FindControlUnderMouse for certain controls. |
// Decides which helper function to call based on the control definition |
// procedure resource ID. The truly interesting stuff happens in the helper |
// functions. |
// |
OSStatus err = noErr; |
if (HaveAppearance ( )) |
*whichControl = FindControlUnderMouse (where,window,cpc); |
else |
*cpc = FindControl (where,window,whichControl); |
if (*whichControl) |
{ |
if (!(MoreAssertPCG (window == GetControlOwner (*whichControl)))) |
err = paramErr; |
else |
{ |
short resID; |
if (!(err = GetControlDefProcResID (*whichControl,&resID))) |
{ |
if (HaveAppearance ( )) |
{ |
switch (resID) |
{ |
#if ! TARGET_API_MAC_CARBON |
case kControlTabsDefProcResID : |
err = SpoofFindControlUnderMouseForTabs (where,window,cpc,*whichControl); |
break; |
#endif |
case kControlPopupButtonDefProcResID : |
err = SpoofFindControlUnderMouseForPopUpButton (cpc); |
break; |
} |
} |
else |
{ |
// here we would work around bugs in pre-Appearance control definitions |
} |
} |
} |
} |
return err; |
} |
pascal OSStatus Get1NewControl (short resID, WindowRef window, ControlHandle *control) |
{ |
// |
// Simple glue for Get1Resource. It only does two extra things. It verifies |
// the control resource with the specified exists in the topmost resource |
// file in the search chain and it releases the contol resource after |
// GetNewControl is done with it. I have an aversion to purgeable handles, |
// because they obfuscate leak detection, so I tend to write code like this. |
// |
OSStatus err = noErr; |
Handle controlResource = Get1Resource (kControlTemplateResourceType,resID); |
if (!controlResource) |
{ |
err = ResError ( ); |
if (!err) err = resNotFound; |
} |
else |
{ |
*control = GetNewControl (resID,window); |
if (!*control) |
err = nilHandleErr; |
ReleaseResource (controlResource); |
assert(ResError ( ) == noErr); |
} |
return err; |
} |
pascal OSStatus SetControlTextFromResource (ControlHandle control, short textResID) |
{ |
OSStatus err = noErr; |
short controlDefProcResID; |
if (MoreAssertPCG (!(err = GetControlDefProcResID (control,&controlDefProcResID)))) |
{ |
if (!MoreAssertPCG (controlDefProcResID == kControlStaticTextDefProcResID || controlDefProcResID == kControlEditTextDefProcResID)) |
err = paramErr; |
else |
{ |
Handle text = Get1Resource ('TEXT',textResID); |
if (!text) |
{ |
err = ResError ( ); |
if (!err) err = resNotFound; |
} |
else |
{ |
Size size = GetHandleSize (text); |
if (MoreAssertPCG (MemError ( ) == noErr)) |
{ |
HLock (text); |
if (MoreAssertPCG (MemError ( ) == noErr)) |
err = SetControlData (control,kControlNoPart,kControlStaticTextTextTag,size,*text); |
} |
ReleaseResource (text); |
assert(ResError ( ) == noErr); |
} |
} |
} |
return err; |
} |
pascal OSStatus SetControlTextStyleFromResource (ControlHandle control, short resID) |
{ |
OSStatus err = noErr; |
Handle controlStyle = Get1Resource (kMoreControls_ControlStyleType, resID); |
if (!controlStyle) |
{ |
err = ResError ( ); |
if (!err) err = resNotFound; |
} |
else |
{ |
Size size = GetHandleSize (controlStyle); |
if (MoreAssertPCG (MemError ( ) == noErr)) |
{ |
if (!MoreAssertPCG (size == sizeof (ControlFontStyleRec))) |
err = paramErr; |
else |
{ |
HLock (controlStyle); |
if (MoreAssertPCG (MemError ( ) == noErr)) |
err = SetControlFontStyle (control, (const ControlFontStyleRec *) *controlStyle); |
} |
} |
ReleaseResource (controlStyle); |
assert(ResError ( ) == noErr); |
} |
return err; |
} |
pascal OSStatus Get1NewStaticTextControl (short resID, WindowRef window, ControlHandle *control) |
{ |
// |
// Since static text controls do not contain data describing their contents |
// or style, this function assumes the calling programmer has created (or chosen |
// not to) text and style resources with the same ID as the control resource. |
// |
OSStatus err = noErr; |
if (!MoreAssertPCG (HaveAppearance ( ))) |
err = paramErr; |
else if (!(err = Get1NewControl (resID,window,control))) |
{ |
err = SetControlTextFromResource (*control,resID); |
if (err == resNotFound) |
err = noErr; |
if (!err) |
{ |
err = SetControlTextStyleFromResource (*control,resID); |
if (err == resNotFound) |
err = noErr; |
} |
if (err) |
{ |
DisposeControl (*control); |
*control = NULL; |
} |
} |
return err; |
} |
pascal OSStatus Get1NewPopupButtonControl (short resID, WindowRef window, ControlHandle *control) |
{ |
// |
// Some versions of Appearance (as of this writing, 1.0.2 and earlier) |
// have a bug in the popup menu button CDEF which causes a crash on |
// GetBestControlRect if the menu ID specified at creation of the control |
// does not exist (and is not -12345). Luckily, we can test for this |
// condition before it's too late. If the CDEF gets fixed, it's unlikely |
// that the fix will involve setting contrlData to NIL. |
// |
OSStatus err = noErr; |
if (!MoreAssertPCG (HaveAppearance ( ))) |
err = paramErr; |
else if (!(err = Get1NewControl (resID,window,control))) |
{ |
if (!MoreAssertControlHasDefProcResID (*control,kControlPopupButtonDefProcResID)) |
err = paramErr; |
else if (!MoreAssertPCG (GetControlDataHandle (*control))) // will fire if non-existent menu ID specified |
err = paramErr; |
if (err) |
DisposeControl (*control); |
} |
return err; |
} |
pascal OSStatus SetUserPaneDrawProc (ControlHandle control, ControlUserPaneDrawUPP upp) |
{ |
// |
// This is just convenience glue to avoid stringing a bunch of long |
// constant names together all the time. |
// |
OSStatus err = noErr; |
if (!MoreAssertPCG (HaveAppearance ( ))) |
err = paramErr; |
else if (!MoreAssertControlHasDefProcResID (control,kControlUserPaneDefProcResID)) |
err = paramErr; |
else |
err = SetControlData (control,kControlNoPart,kControlUserPaneDrawProcTag,sizeof(upp),(Ptr)&upp); |
return err; |
} |
pascal OSStatus SetUserPaneSetUpSpecialBackgroundProc (ControlHandle control, ControlUserPaneBackgroundUPP upp) |
{ |
// |
// This is just convenience glue to avoid stringing a bunch of long |
// constant names together all the time. |
// |
OSStatus err = noErr; |
if (!MoreAssertPCG (HaveAppearance ( ))) |
err = paramErr; |
else if (!MoreAssertControlHasDefProcResID (control,kControlUserPaneDefProcResID)) |
err = paramErr; |
else |
err = SetControlData (control,kControlNoPart,kControlUserPaneBackgroundProcTag,sizeof(upp),(Ptr)&upp); |
return err; |
} |
pascal OSStatus SetUserPaneTrackingProc (ControlHandle control, ControlUserPaneTrackingUPP upp) |
{ |
// |
// This is just convenience glue to avoid stringing a bunch of long |
// constant names together all the time. |
// |
OSStatus err = noErr; |
if (!MoreAssertPCG (HaveAppearance ( ))) |
err = paramErr; |
else if (!MoreAssertControlHasDefProcResID (control,kControlUserPaneDefProcResID)) |
err = paramErr; |
else |
err = SetControlData (control,kControlNoPart,kControlUserPaneTrackingProcTag,sizeof(upp),(Ptr)&upp); |
return err; |
} |
static pascal OSStatus GetEnclosingBounds (ControlHandle control, Rect *bounds) |
{ |
// |
// Gets the bounds of the specified control's super-control. |
// the the specified control has no super-control or the specified |
// control's super control is the root control (whose bounds |
// encompass all of QuickDraw, which is usually not useful), this |
// function gets the bounds of the owning window. |
// |
OSStatus err = noErr; |
WindowRef controlOwner = GetControlOwner (control); |
if (!MoreAssertPCG (control && controlOwner)) |
err = nilHandleErr; |
else if (!HaveAppearance ( )) |
GetWindowPortBounds (controlOwner,bounds); |
else |
{ |
ControlHandle superControl; |
err = GetSuperControl (control,&superControl); |
if (err == errNoRootControl) |
{ |
GetControlBounds (control,bounds); |
err = noErr; |
} |
else if (!err) |
{ |
ControlHandle rootControl; |
if (!(err = GetRootControl (controlOwner, &rootControl))) |
{ |
if (rootControl == superControl) |
GetWindowPortBounds (controlOwner,bounds); |
else |
GetControlBounds (superControl,bounds); |
} |
} |
} |
return err; |
} |
pascal OSStatus ExpandControl (ControlHandle control) |
{ |
// |
// Expands the bounds of the specified control to fill its enclosing rectangle. |
// |
OSStatus err = noErr; |
if (!MoreAssertPCG (control)) |
err = nilHandleErr; |
else |
{ |
Rect bounds; |
if (!(err = GetEnclosingBounds (control,&bounds))) |
{ |
MoveControl (control, bounds.left, bounds.top); |
SizeControl (control, (short) (bounds.right - bounds.left), (short) (bounds.bottom - bounds.top)); |
} |
} |
return err; |
} |
pascal OSStatus InsetControl (ControlHandle control, SInt16 byHowMuchH, SInt16 byHowMuchV) |
{ |
// |
// Insets a control just like InsetRect insets a Rect. |
// |
OSStatus err = noErr; |
if (!MoreAssertPCG (control)) |
err = nilHandleErr; |
else |
{ |
Rect contrlRect; |
GetControlBounds (control,&contrlRect); |
InsetRect (&contrlRect, byHowMuchH, byHowMuchV); |
MoveControl (control, contrlRect.left, contrlRect.top); |
SizeControl (control, (short) (contrlRect.right - contrlRect.left), (short) (contrlRect.bottom - contrlRect.top)); |
} |
return err; |
} |
pascal OSStatus OffsetControl (ControlHandle control, SInt16 h, SInt16 v) |
{ |
// |
// Offsets a control just like OffsetRect offsets a Rect. |
// |
OSStatus err = noErr; |
if (!MoreAssertPCG (control)) |
err = nilHandleErr; |
else |
{ |
Rect contrlRect; |
GetControlBounds (control,&contrlRect); |
OffsetRect (&contrlRect,h,v); |
MoveControl (control,contrlRect.left,contrlRect.top); |
} |
return err; |
} |
pascal OSStatus MoveControlNear (ControlHandle moveMe, ControlHandle near, MoreControls_tDirection dir) |
{ |
// |
// Moves one control near another. If the controls both have radio behavior, |
// the distance between them is 0. If at least one of the controls does not |
// have radio behavior, the distance between the two controls is non-zero |
// (but not large; see definition of MoreControls_kControlGap). |
// |
// To understand the relative positioning, see the comments in MoreControls_tDirection. |
// |
// Right now this function only supports north, south, east, and west. |
// I can't decide what it means to move a control to the southwest of another. |
// |
OSStatus err = noErr; |
if (!MoreAssertPCG (moveMe && near)) |
err = nilHandleErr; |
else if (!MoreAssertPCG (GetControlOwner (moveMe) == GetControlOwner (near))) |
err = paramErr; |
else if (!MoreAssertPCG (dir == MoreControls_kDirectionNorth || dir == MoreControls_kDirectionSouth || |
dir == MoreControls_kDirectionEast || dir == MoreControls_kDirectionWest)) |
{ |
err = paramErr; |
} |
else |
{ |
RgnHandle moveMeRgn = NULL, |
nearRgn = NULL; |
if (!(err = MoreGetControlRegion (moveMe,&moveMeRgn))) |
if (!(err = MoreGetControlRegion (near,&nearRgn))) |
{ |
Rect moveMeRect, nearRect; |
GetRegionBounds (moveMeRgn,&moveMeRect); |
GetRegionBounds (nearRgn,&nearRect); |
SInt16 moveMeWidth = (short) (moveMeRect.right - moveMeRect.left), |
moveMeHeight = (short) (moveMeRect.bottom - moveMeRect.top), |
nearWidth = (short) (nearRect.right - nearRect.left), |
nearHeight = (short) (nearRect.bottom - nearRect.top); |
switch (dir) |
{ |
case MoreControls_kDirectionNorth : |
case MoreControls_kDirectionSouth : |
moveMeRect.left = (short) (nearRect.left + (nearWidth / 2) - (moveMeWidth / 2)); |
moveMeRect.right = (short) (moveMeRect.left + moveMeWidth); |
break; |
case MoreControls_kDirectionEast : |
case MoreControls_kDirectionWest : |
moveMeRect.top = (short) (nearRect.top + (nearHeight / 2) - (moveMeHeight / 2)); |
moveMeRect.bottom = (short) (moveMeRect.top + moveMeHeight); |
break; |
} |
switch (dir) |
{ |
case MoreControls_kDirectionWest : |
moveMeRect.right = (short) (nearRect.left - MoreControls_kControlGap); |
moveMeRect.left = (short) (moveMeRect.right - moveMeWidth); |
break; |
case MoreControls_kDirectionEast : |
moveMeRect.left = (short) (nearRect.right + MoreControls_kControlGap); |
moveMeRect.right = (short) (moveMeRect.left + moveMeWidth); |
break; |
case MoreControls_kDirectionNorth : |
moveMeRect.bottom = (short) (nearRect.top - MoreControls_kControlGap); |
moveMeRect.top = (short) (moveMeRect.bottom - moveMeHeight); |
break; |
case MoreControls_kDirectionSouth : |
moveMeRect.top = (short) (nearRect.bottom + MoreControls_kControlGap); |
moveMeRect.bottom = (short) (moveMeRect.top + moveMeHeight); |
break; |
} |
MoveControl (moveMe, moveMeRect.left, moveMeRect.top); // finally, the pay-off! |
} |
if (moveMeRgn) |
{ |
DisposeRgn (moveMeRgn); |
assert(noErr == QDError ( )); |
} |
if (nearRgn) |
{ |
DisposeRgn (nearRgn); |
assert(noErr == QDError ( )); |
} |
} |
return err; |
} |
pascal OSStatus SetControlQuadrant (ControlHandle control, MoreControls_tDirection quadrant) |
{ |
// |
// Moves a control to one of nine quadrants within its enclosing rectangle. |
// To understand the positioning, see the comments in MoreControls_tDirection. |
// |
OSStatus err = noErr; |
// Previously this used to be: |
// |
// if (!(MoreAssertPCG (quadrant >= MoreControls_kDirectionNorthWest && quadrant <= MoreControls_kDirectionSouthEast))) |
// |
// about which gcc complains that "the comparison is always true due to |
// the limited range of the data type". This is usually a good warning, |
// but in this case the code was correct. However, because I donÕt want |
// to turn the warning off I just changed the code to the less neat but |
// still helpful asserts shown below. |
// -- Quinn, 8 Feb 2001 |
assert(MoreControls_kDirectionNorthWest == 0); |
if (!(MoreAssertPCG (quadrant <= MoreControls_kDirectionSouthEast))) |
err = paramErr; |
else |
{ |
Rect enclosingBounds; |
if (!(err = GetEnclosingBounds (control,&enclosingBounds))) |
{ |
Rect controlRect; |
GetControlBounds (control,&controlRect); |
SInt16 controlWidth = (short) (controlRect.right - controlRect.left), |
controlHeight = (short) (controlRect.bottom - controlRect.top), |
enclosingWidth = (short) (enclosingBounds.right - enclosingBounds.left), |
enclosingHeight = (short) (enclosingBounds.bottom - enclosingBounds.top); |
switch (quadrant) |
{ |
case MoreControls_kDirectionNorthWest : |
case MoreControls_kDirectionNorth : |
case MoreControls_kDirectionNorthEast : |
controlRect.top = enclosingBounds.top; |
controlRect.bottom = (short) (enclosingBounds.top + controlHeight); |
break; |
case MoreControls_kDirectionSouthWest : |
case MoreControls_kDirectionSouth : |
case MoreControls_kDirectionSouthEast : |
controlRect.bottom = enclosingBounds.bottom; |
controlRect.top = (short) (enclosingBounds.bottom - controlHeight); |
break; |
case MoreControls_kDirectionWest : |
case MoreControls_kDirectionCenter : |
case MoreControls_kDirectionEast : |
controlRect.top = (short) (enclosingBounds.top + (enclosingHeight / 2) - (controlHeight / 2)); |
controlRect.bottom = (short) (controlRect.top + controlHeight); |
break; |
} |
switch (quadrant) |
{ |
case MoreControls_kDirectionNorthWest : |
case MoreControls_kDirectionWest : |
case MoreControls_kDirectionSouthWest : |
controlRect.left = enclosingBounds.left; |
controlRect.right = (short) (enclosingBounds.left + controlWidth); |
break; |
case MoreControls_kDirectionNorthEast : |
case MoreControls_kDirectionEast : |
case MoreControls_kDirectionSouthEast : |
controlRect.right = enclosingBounds.right; |
controlRect.left = (short) (enclosingBounds.right - controlWidth); |
break; |
case MoreControls_kDirectionNorth : |
case MoreControls_kDirectionCenter : |
case MoreControls_kDirectionSouth : |
controlRect.left = (short) (enclosingBounds.left + (enclosingWidth / 2) - (controlWidth / 2)); |
controlRect.right = (short) (controlRect.left + controlWidth); |
break; |
} |
MoveControl (control, controlRect.left, controlRect.top); |
} |
} |
return err; |
} |
pascal OSStatus SetBestControlRect (ControlHandle control, SInt16 *baseLineOffsetP) |
{ |
// |
// Takes GetBestControlRect to its logical conclusion and moves and resizes |
// the specified control to fit its best control bounds rectangle, if any. |
// You can pass NIL for baseLineOffsetP if you don't care to know the text |
// baseline for the control. This function should probably produce a value |
// denoting whether the control moved or changed size. |
// |
OSStatus err = noErr; |
// it's OK for baseLineOffsetP to be NIL |
if (!MoreAssertPCG (control)) |
err = nilHandleErr; |
else if (!MoreAssertPCG (HaveAppearance ( ))) |
err = paramErr; |
else |
{ |
UInt32 features; |
err = GetControlFeatures (control, &features); |
if (!err && (kControlSupportsCalcBestRect & features)) |
{ |
Rect best = { 0,0,0,0 }; |
SInt16 dontCare; |
if (!baseLineOffsetP) |
baseLineOffsetP = &dontCare; |
if (!(err = GetBestControlRect (control,&best,baseLineOffsetP))) |
{ |
SizeControl (control, (short) (best.right - best.left), (short) (best.bottom - best.top) ); |
MoveControl (control, best.left, best.top); |
} |
} |
} |
return err; |
} |
pascal OSStatus GetListBoxControlListHandle (ControlHandle control, ListHandle *result) |
{ |
// |
// This is just convenience glue to avoid stringing a bunch of long |
// constant names together all the time. |
// |
OSStatus err = noErr; |
if (!MoreAssertPCG (HaveAppearance ( ))) |
err = paramErr; |
else if (!MoreAssertControlHasDefProcResID (control,kControlListBoxDefProcResID)) |
err = paramErr; |
else |
{ |
Size actualListHandleSize; |
err = GetControlData (control, kControlNoPart, kControlListBoxListHandleTag, |
sizeof (*result), (Ptr) result, &actualListHandleSize); |
if (!err) assert(actualListHandleSize == sizeof (*result)); |
} |
return err; |
} |
pascal OSStatus GetStaticTextControlTextHeight (ControlHandle control, SInt16 *textHeight) |
{ |
// |
// This is just convenience glue to avoid stringing a bunch of long |
// constant names together all the time. |
// |
OSStatus err = noErr; |
if (!MoreAssertPCG (HaveAppearance ( ))) |
err = paramErr; |
else if (!MoreAssertControlHasDefProcResID (control,kControlStaticTextDefProcResID)) |
err = paramErr; |
else |
{ |
Size actualTextHeightSize; |
err = GetControlData (control, kControlNoPart, kControlStaticTextTextHeightTag, |
sizeof (*textHeight), (Ptr) textHeight, &actualTextHeightSize); |
if (!err) assert(actualTextHeightSize == sizeof (*textHeight)); |
} |
return err; |
} |
pascal OSStatus SetBevelButtonMenu (ControlHandle control, MenuRef menu) |
{ |
// |
// This is just convenience glue to avoid stringing a bunch of long |
// constant names together all the time. |
// |
OSStatus err = noErr; |
if (!MoreAssertPCG (HaveAppearance ( ))) |
err = paramErr; |
else if (!MoreAssertControlHasDefProcResID (control,kControlBevelButtonDefProcResID)) |
err = paramErr; |
else |
{ |
// |
// There appears to be a bug in the bevel button CDEF which prevents you |
// from setting a menu if the control was not created with a menu, so to |
// avoid this problem, we assert so you can tell when you are likely to |
// run afoul of this problem. I can't remember whether I filed a bug |
// against this misbehavior. I should check. |
// |
#if MORE_DEBUG |
MenuHandle existingMenuH; |
Size actualSize; |
err = GetControlData (control, kControlNoPart, kControlBevelButtonMenuHandleTag, |
sizeof(existingMenuH), Ptr (&existingMenuH), &actualSize); |
if (err) return err; |
if (!MoreAssertPCG (actualSize == sizeof(existingMenuH))) |
err = paramErr; |
else if (!MoreAssertPCG (existingMenuH)) |
err = nilHandleErr; |
else |
#endif |
err = SetControlData (control, kControlNoPart, kControlBevelButtonMenuHandleTag, |
sizeof(MenuRef), Ptr (&menu)); |
} |
return err; |
} |
pascal OSStatus SetPopUpButtonMenu (ControlHandle control, MenuRef menu) |
{ |
// |
// This is just convenience glue to avoid stringing a bunch of long |
// constant names together all the time. |
// |
assert(HaveAppearance ( )); |
assert(GetAppearanceVersion ( ) >= 0x0101); |
MoreAssertControlHasDefProcResID (control,kControlPopupButtonDefProcResID); |
OSStatus err = SetControlData (control, kControlNoPart, kControlPopupButtonMenuHandleTag, |
sizeof(MenuRef), Ptr (&menu)); |
return err; |
} |
pascal OSStatus SetBevelButtonMenuDelay (ControlHandle control, SInt32 delay) |
{ |
// |
// This is just convenience glue to avoid stringing a bunch of long |
// constant names together all the time. |
// |
OSStatus err = noErr; |
if (!MoreAssertPCG (HaveAppearance ( ))) |
err = paramErr; |
else if (!MoreAssertPCG (GetAppearanceVersion ( ) >= 0x0101)) |
err = paramErr; |
else if (!MoreAssertControlHasDefProcResID (control,kControlBevelButtonDefProcResID)) |
err = paramErr; |
else |
err = SetControlData (control, kControlNoPart, kControlBevelButtonMenuDelayTag, sizeof (delay), Ptr (&delay)); |
return err; |
} |
pascal OSStatus SetProgressBarIndeterminate (ControlHandle control, Boolean i) |
{ |
// |
// This is just convenience glue to avoid stringing a bunch of long |
// constant names together all the time. |
// |
OSStatus err = noErr; |
if (!MoreAssertPCG (HaveAppearance ( ))) |
err = paramErr; |
else if (!MoreAssertControlHasDefProcResID (control,kControlProgressBarDefProcResID)) |
err = paramErr; |
else if (!MoreAssertPCG (i == true || i == false)) // talk about paranoid! |
err = paramErr; |
else |
err = SetControlData (control, kControlNoPart, kControlProgressBarIndeterminateTag, sizeof (i), Ptr (&i)); |
return err; |
} |
pascal OSStatus ToggleControl (ControlHandle control) |
{ |
// |
// Given a control whose limits suggest it toggles between 0 and 1 |
// (like a check box or a radio button), this function sets the control |
// value to the opposite of its present value. |
// |
OSStatus err = noErr; |
if (!MoreAssertPCG (control)) |
err = nilHandleErr; |
else if (!MoreAssertPCG (GetControlMaximum (control) == 1)) |
err = paramErr; |
else if (!MoreAssertPCG (GetControlMinimum (control) == 0)) |
err = paramErr; |
else |
SetControlValue (control, GetControlValue (control) ? (short) 0 : (short) 1); |
return err; |
} |
static pascal OSStatus EmbedSubControls (ControlHandle source, ControlHandle destination, SInt16 index) |
{ |
// |
// This is a helper function for EncloseSubControls; if you find a use |
// for it elsewhere, keep in mind that the controls are embedded into |
// the destination control in the opposite order from that in which |
// they appear in the source control. This is fine as long as you |
// copy the controls back with EmbedSubControls. This is just what |
// EncloseSubControls needed. Why do we do things this way? We want |
// to be recursive so we can easily back out of a partial embedding |
// transfer, and this is the easiest way to do it. |
// |
OSStatus err = noErr; |
if (index) |
{ |
ControlHandle subControl; |
if (!(err = GetIndexedSubControl (source,(UInt16) index,&subControl))) |
if (!(err = EmbedControl (subControl,destination))) |
{ |
err = EmbedSubControls (source,destination, (short) (index-1) ); |
if (err) (void) EmbedControl (subControl,source); |
} |
} |
return err; |
} |
pascal OSStatus EncloseSubControls (ControlHandle parent) |
{ |
// |
// Moves and resizes the parent control to enclose all its sub-controls. |
// However, we can't just move the parent control, because the |
// sub-controls will move along with it, which is not what we want. |
// So we create a temporary embeddable control and move the sub-controls |
// to this temporary control while we move the parent control. |
// Note that EmbedSubControls reverses the order of the sub-controls, |
// which is fine for our purposes, since we call it twice. |
// |
OSStatus err = noErr; |
if (!MoreAssertPCG (parent)) |
err = nilHandleErr; |
else |
{ |
SInt16 subControlCount; |
if (!(err = CountSubControls (parent,(UInt16 *)&subControlCount))) |
{ |
if (!MoreAssertPCG (subControlCount > 0)) |
err = paramErr; |
else |
{ |
static Rect tempUserPaneBounds; |
WindowPtr controlOwner = GetControlOwner (parent); |
ControlHandle tempUserPane = NewControl (controlOwner, &tempUserPaneBounds, "\p", false, |
kControlSupportsEmbedding, 0, 0, kControlUserPaneProc, 0); |
if (!tempUserPane) |
err = nilHandleErr; |
else |
{ |
SInt16 subControlIndex = subControlCount; |
ControlHandle subControl; |
if (!(err = GetIndexedSubControl (parent,(UInt16)subControlIndex,&subControl))) |
{ |
if (!MoreAssertPCG (subControl)) |
err = nilHandleErr; |
else |
{ |
Rect parentRect; |
GetControlBounds (subControl,&parentRect); |
while (--subControlIndex) |
{ |
err = GetIndexedSubControl (parent,(UInt16)subControlIndex,&subControl); |
if (err) break; |
if (!MoreAssertPCG (subControl)) |
{ |
err = nilHandleErr; |
break; |
} |
Rect contrlRect; |
GetControlBounds (subControl,&contrlRect); |
if (contrlRect.bottom > parentRect.bottom) |
parentRect.bottom = contrlRect.bottom; |
if (contrlRect.right > parentRect.right) |
parentRect.right = contrlRect.right; |
if (contrlRect.left < parentRect.left) |
parentRect.left = contrlRect.left; |
if (contrlRect.top < parentRect.top) |
parentRect.top = contrlRect.top; |
} |
if (!err && !(err = EmbedSubControls (parent,tempUserPane,subControlCount))) |
{ |
InsetRect (&parentRect, -(MoreControls_kControlGap), -(MoreControls_kControlGap)); |
MoveControl (parent, parentRect.left, parentRect.top); |
SizeControl (parent, (short) (parentRect.right - parentRect.left), (short) (parentRect.bottom - parentRect.top)); |
OSStatus err2 = EmbedSubControls (tempUserPane,parent,subControlCount); |
if (!err) err2 = err; |
} |
} |
} |
DisposeControl (tempUserPane); |
} |
} |
} |
} |
return err; |
} |
pascal OSStatus AlignControl (ControlHandle anchor, ControlHandle moveMe, MoreControls_tAlignment align) |
{ |
// |
// Aligns one control (moveMe) to an edge of another (anchor). |
// For an explanation of MoreControls_tAlignment, see the header. |
// |
OSStatus err = noErr; |
if (!MoreAssertPCG (moveMe && anchor)) |
err = nilHandleErr; |
else if (!MoreAssertPCG (align == MoreControls_kAlignSystem || align == MoreControls_kAlignLeft || |
align == MoreControls_kAlignCenter || align == MoreControls_kAlignRight)) |
{ |
err = paramErr; |
} |
else |
{ |
align = FilterAlignment (align); |
if (!MoreAssertPCG (align == MoreControls_kAlignLeft || |
align == MoreControls_kAlignCenter || align == MoreControls_kAlignRight)) |
{ |
err = paramErr; |
} |
else |
{ |
Rect moveRect, anchorRect; |
GetControlBounds (moveMe,&moveRect); |
GetControlBounds (anchor,&anchorRect); |
short horiz, vert = moveRect.top; |
switch (align) |
{ |
case MoreControls_kAlignLeft : |
horiz = anchorRect.left; |
break; |
case MoreControls_kAlignCenter : |
assert(false && MoreControls_kAlignCenter); // untraced logic |
break; |
case MoreControls_kAlignRight : |
assert(false && MoreControls_kAlignRight); // untraced logic |
horiz = (short) (anchorRect.right - (moveRect.right - moveRect.left)); |
break; |
} |
MoveControl (moveMe, horiz, vert); |
} |
} |
return err; |
} |
pascal OSStatus AlignSubControls (ControlHandle parent, MoreControls_tAlignment align) |
{ |
// |
// Given a parent control, aligns all its sub-controls along the most |
// extreme edge. So if left alignment is specified, this function finds |
// the sub-control which is leftmost and aligns the rest of the sub-controls |
// to its left edge. For an explanation of MoreControls_tAlignment, see the header. |
// |
OSStatus err = noErr; |
if (!MoreAssertPCG (parent)) |
err = nilHandleErr; |
else if (!MoreAssertPCG (align == MoreControls_kAlignSystem || align == MoreControls_kAlignLeft || |
align == MoreControls_kAlignCenter || align == MoreControls_kAlignRight)) |
{ |
err = paramErr; |
} |
else |
{ |
align = FilterAlignment (align); |
if (!MoreAssertPCG (align == MoreControls_kAlignLeft || |
align == MoreControls_kAlignCenter || align == MoreControls_kAlignRight)) |
{ |
err = paramErr; |
} |
else |
{ |
UInt16 subControlCount; |
if (!(err = CountSubControls (parent,&subControlCount))) |
{ |
if (!MoreAssertPCG (subControlCount > 0)) |
err = paramErr; |
else if (subControlCount == 1) |
{ |
assert(false && subControlCount == 1); // untraced logic |
} |
else |
{ |
UInt16 subControlIndex = subControlCount; |
ControlHandle subControl; |
if (!(err = GetIndexedSubControl (parent,subControlIndex,&subControl))) |
{ |
if (!MoreAssertPCG (subControl)) |
err = nilHandleErr; |
else |
{ |
ControlHandle anchor = subControl; |
while (--subControlIndex) |
{ |
err = GetIndexedSubControl (parent,subControlIndex,&subControl); |
if (err) break; |
Rect subRect, anchorRect; |
GetControlBounds (subControl,&subRect); |
GetControlBounds (anchor,&anchorRect); |
switch (align) |
{ |
case MoreControls_kAlignLeft : |
if (subRect.left < anchorRect.left) |
anchor = subControl; |
break; |
case MoreControls_kAlignCenter : |
assert(false && MoreControls_kAlignCenter); // untraced logic |
break; |
case MoreControls_kAlignRight : |
if (subRect.right > anchorRect.right) |
anchor = subControl; |
break; |
} |
} |
if (!err) |
{ |
subControlIndex = subControlCount; |
do |
{ |
err = GetIndexedSubControl (parent,subControlIndex,&subControl); |
if (err) break; |
Rect subRect, anchorRect; |
GetControlBounds (subControl,&subRect); |
GetControlBounds (anchor,&anchorRect); |
if (subControl != anchor) |
{ |
short horiz, vert = subRect.top; |
switch (align) |
{ |
case MoreControls_kAlignLeft : |
horiz = anchorRect.left; |
break; |
case MoreControls_kAlignCenter : |
assert(false && MoreControls_kAlignCenter); // untraced logic |
break; |
case MoreControls_kAlignRight : |
horiz = (short) (anchorRect.right - (subRect.right - subRect.left)); |
break; |
} |
MoveControl (subControl,horiz,vert); |
} |
} |
while (--subControlIndex); |
} |
} |
} |
} |
} |
} |
} |
return err; |
} |
pascal OSStatus IdleControlsInAllWindows (void) |
{ |
// |
// For each visible window in the window list, this function gives |
// the controls a chance to idle. |
// |
OSStatus err = noErr; |
if (!MoreAssertPCG (HaveAppearance ( ))) |
err = paramErr; |
else if (WindowPtr window = FrontWindow ( )) |
{ |
IdleControls (window); |
window = GetNextWindow (window); |
while (window) |
{ |
if (IsWindowVisible (window)) |
IdleControls (window); |
window = GetNextWindow (window); |
} |
} |
return err; |
} |
pascal OSStatus SetCursorAccordingToControl (void) |
{ |
// |
// Sets the cursor according to which control the cursor |
// is floating over. In a perfect world, we'd be able to |
// ask the control to set the cursor for us, but in the |
// real world we just guess. |
// |
OSStatus err = noErr; |
WindowPtr window = FrontWindow ( ); |
if (!window) |
SetArrowCursor ( ); |
else |
{ |
GrafPtr preservedPort; |
GetPort (&preservedPort); |
SetPortWindowPort (window); |
Point mouse; |
GetMouse (&mouse); |
ControlPartCode cpc; |
ControlHandle whichControl; |
err = MoreFindControlUnderMouse (mouse, window, &cpc, &whichControl); |
if (!err) |
{ |
if (!whichControl || cpc == kControlNoPart || !HaveAppearance ( )) |
SetArrowCursor ( ); |
else |
{ |
short resID; |
if (!(err = GetControlDefProcResID (whichControl, &resID))) |
{ |
if (resID == kControlEditTextDefProcResID) |
SetCursor (*GetCursor (iBeamCursor)); |
else |
SetArrowCursor ( ); |
} |
} |
} |
SetPort (preservedPort); |
} |
return err; |
} |
pascal OSStatus IsScrollBar (ControlHandle ch, Boolean *isScrollBar) |
{ |
// |
// Tests whether a given control is a scroll bar. |
// Replaces bogus code from AppsToGo. TO DO: find |
// a better abstraction for this. Waiting for |
// developer feedback. This function may go away. |
// |
OSStatus err = noErr; |
if (!MoreAssertPCG (ch && isScrollBar)) |
err = nilHandleErr; |
else |
{ |
short resID; |
if (!(err = GetControlDefProcResID (ch, &resID))) |
{ |
*isScrollBar = (resID == kControlOldScrollBarDefProcResID || resID == kControlScrollBarDefProcResID); |
} |
} |
return err; |
} |
pascal OSStatus ActivateControls (WindowPtr w) |
{ |
OSStatus err = noErr; |
if (!MoreAssertPCG (HaveAppearance ( ))) |
err = paramErr; |
else |
{ |
ControlHandle root; |
if (!(err = GetRootControl (w, &root))) |
{ |
err = ActivateControl (root); |
} |
} |
return err; |
} |
pascal OSStatus DeactivateControls (WindowPtr w) |
{ |
OSStatus err = noErr; |
if (!MoreAssertPCG (HaveAppearance ( ))) |
err = paramErr; |
else |
{ |
ControlHandle root; |
if (!(err = GetRootControl (w, &root))) |
{ |
err = DeactivateControl (root); |
} |
} |
return err; |
} |
extern OSStatus GetControlByIDQ(WindowRef inWindow, OSType signature, SInt32 id, ControlRef *outControl) |
{ |
ControlID theID; |
theID.signature = signature; |
theID.id = id; |
return GetControlByID(inWindow, &theID, outControl); |
} |
#if TARGET_API_MAC_CARBON |
static OSType kMainTags[2] = { kControlEditTextCFStringTag, kControlEditTextPasswordCFStringTag }; |
static OSType kCompatTags[2] = { kControlEditTextTextTag, kControlEditTextPasswordTag }; |
extern pascal OSStatus CopyTextControlTextCompat(ControlRef control, Boolean isPassword, CFStringRef *textStr) |
{ |
OSStatus err; |
assert(control != NULL); |
assert( textStr != NULL); |
assert(*textStr == NULL); |
err = GetControlData(control, kControlEntireControl, kMainTags[isPassword], sizeof(*textStr), textStr, NULL); |
if (err != noErr) { |
Size dataSize; |
char *dataBuffer; |
dataBuffer = NULL; |
err = GetControlData(control, kControlEntireControl, kCompatTags[isPassword], 0, NULL, &dataSize); |
if (err == noErr) { |
dataBuffer = NewPtr(dataSize); |
err = MoreMemError(dataBuffer); |
} |
if (err == noErr) { |
err = GetControlData(control, kControlEntireControl, kCompatTags[isPassword], dataSize, dataBuffer, &dataSize); |
} |
if (err == noErr) { |
*textStr = CFStringCreateWithBytes(NULL, (UInt8 *) dataBuffer, dataSize, GetApplicationTextEncoding(), false); |
err = CFQError(*textStr); |
} |
if (dataBuffer != NULL) { |
DisposePtr(dataBuffer); |
assert(MemError() == noErr); |
} |
} |
assert( (err == noErr) == (*textStr != NULL) ); |
return err; |
} |
static Boolean GetControlEncoding(ControlRef inControl, CFStringEncoding* outEncoding) |
// Get the text encoding used by the control. |
{ |
OSStatus junk; |
Boolean gotFont; |
ControlFontStyleRec fontStyle; |
short fID; |
long oldFontForce; |
gotFont = false; |
// First see if we have a font ID. |
if ( GetControlData( inControl, kControlEntireControl, kControlFontStyleTag, |
sizeof( fontStyle ), &fontStyle, NULL ) == noErr ) { |
if ( (fontStyle.flags & kControlUseFontMask) != 0 ) { |
fID = fontStyle.font; |
gotFont = true; |
} |
} |
// If not, see whether we're using the window's font. |
if ( !gotFont && ( GetControlVariant(inControl) & kControlUsesOwningWindowsFontVariant ) != 0 ) { |
fID = GetPortTextFont(GetWindowPort(GetControlOwner(inControl))); |
gotFont = true; |
} |
// If we managed to get a font ID, map that to its script system |
// using FontToScript. Note that we have to disable smFontForce |
// to get accurate results (otherwise FontToScript might base its |
// response on the current port!). |
if (gotFont) { |
oldFontForce = GetScriptManagerVariable(smFontForce); |
junk = SetScriptManagerVariable(smFontForce, 0); |
assert(junk == noErr); |
*outEncoding = FontToScript(fID); |
junk = SetScriptManagerVariable(smFontForce, oldFontForce); |
assert(junk == noErr); |
} |
return gotFont; |
} |
static OSStatus ConvertControlCFStringToPascalString(ControlRef inControl, CFStringRef inText, StringPtr outString) |
// Converts a CFString to a Pascal string in the context of the control. |
{ |
OSStatus err; |
CFStringEncoding encoding; |
ByteCount textLength; |
assert(inControl != NULL); |
assert(inText != NULL); |
assert(outString != NULL); |
err = noErr; |
// First, check if the control has a font, or if it's useWFont, and try to extract |
// the title using that encoding. |
outString[0] = 0; |
if ( GetControlEncoding(inControl, &encoding) ) { |
CFStringGetPascalString(inText, outString, sizeof(Str255), encoding); |
} |
// If the control is just drawn with the system font, or we failed to extract the title |
// using the control's font encoding, then fall back to CFStringGetBestEncodingAndString. |
if ( outString[0] == 0 ) { |
err = GetTextAndEncodingFromCFString(inText, &outString[1], 255, &textLength, &encoding); |
if (err == noErr || err == kTECOutputBufferFullStatus) { |
outString[0] = (UInt8) textLength; |
} |
} |
return err; |
} |
extern pascal OSStatus SetTextControlTextCompat(ControlRef control, Boolean isPassword, CFStringRef textStr) |
{ |
OSStatus err; |
assert(control != NULL); |
assert( textStr != NULL); |
err = SetControlData(control, kControlEntireControl, kMainTags[isPassword], sizeof(textStr), &textStr); |
if ( err != noErr ) { |
Str255 tmpStr; |
// Do it the hard way. This compatibility code was largely stolen from the |
// code in CarbonLib. |
err = ConvertControlCFStringToPascalString(control, textStr, tmpStr); |
if (err == noErr) { |
err = SetControlData(control, kControlEntireControl, kCompatTags[isPassword], tmpStr[0], &tmpStr[1]); |
} |
} |
return err; |
} |
#endif |
extern pascal OSStatus InstallControlKeyFilter(ControlRef control, OSType myTag, ControlKeyFilterUPP newFilter) |
{ |
OSStatus err; |
ControlKeyFilterUPP oldFilter; |
err = GetControlData(control, kControlEntireControl, kControlKeyFilterTag, sizeof(oldFilter), &oldFilter, NULL); |
if (err == noErr) { |
err = SetControlProperty(control, 'MIB!', myTag, sizeof(oldFilter), &oldFilter); |
} |
if (err == noErr) { |
err = SetControlData(control, kControlEntireControl, kControlKeyFilterTag, sizeof(newFilter), &newFilter); |
} |
return err; |
} |
extern pascal ControlKeyFilterResult CallNextControlKeyFilter(ControlRef control, OSType myTag, SInt16 *keyCode, SInt16 *charCode, EventModifiers *modifiers) |
{ |
ControlKeyFilterResult result; |
ControlKeyFilterUPP oldFilter; |
result = kControlKeyFilterPassKey; |
if ( GetControlProperty(control, 'MIB!', myTag, sizeof(oldFilter), NULL, &oldFilter) != noErr ) { |
oldFilter = NULL; |
} |
if (oldFilter != NULL) { |
result = InvokeControlKeyFilterUPP(control, keyCode, charCode, modifiers, oldFilter); |
} |
return result; |
} |
static ControlKeyFilterUPP gControlNoReturnsKeyFilterUPP; // -> NoReturnsControlKeyFilter |
extern pascal ControlKeyFilterUPP GetControlNoReturnsKeyFilterUPP(void) |
{ |
if (gControlNoReturnsKeyFilterUPP == NULL) { |
gControlNoReturnsKeyFilterUPP = NewControlKeyFilterUPP(ControlNoReturnsKeyFilter); |
} |
return gControlNoReturnsKeyFilterUPP; |
} |
extern pascal ControlKeyFilterResult ControlNoReturnsKeyFilter(ControlRef theControl, SInt16 *keyCode, SInt16 *charCode, EventModifiers *modifiers) |
{ |
ControlKeyFilterResult result; |
result = kControlKeyFilterPassKey; |
if (*charCode == kReturnCharCode) { |
result = kControlKeyFilterBlockKey; |
} |
if (result == kControlKeyFilterPassKey) { |
result = CallNextControlKeyFilter(theControl, kMIBControlNoReturnsKeyFilterKeyFilterTag, keyCode, charCode, modifiers); |
} |
return result; |
} |
#if TARGET_API_MAC_CARBON |
#if UNIVERSAL_INTERFACES_VERSION < 0x0400 |
enum { |
/* |
* This event parameter may be added to any event that is posted to |
* the main event queue. When the event is removed from the queue and |
* sent to the event dispatcher, the dispatcher will retrieve the |
* EventTargetRef contained in this parameter and send the event |
* directly to that event target. If this parameter is not available |
* in the event, the dispatcher will send the event to a suitable |
* target, or to the application target if no more specific target is |
* appropriate. Available in CarbonLib 1.3.1 and later, and Mac OS X. |
*/ |
kEventParamPostTarget = 'ptrg', /* typeEventTargetRef*/ |
/* |
* Indicates an event parameter of type EventTargetRef. |
*/ |
typeEventTargetRef = 'etrg' /* EventTargetRef*/ |
}; |
#endif |
static OSStatus PostControlEditTextModifiedEvent(ControlRef control) |
{ |
OSStatus err; |
EventTargetRef target; |
EventRef event; |
assert(control != NULL); |
event = NULL; |
target = GetControlEventTarget(control); |
assert(target != NULL); |
err = MacCreateEvent(NULL, kMoreIsBetterEventClass, kMIBControlEditTextModifiedKind, GetCurrentEventTime(), kEventAttributeNone, &event); |
if (err == noErr) { |
err = SetEventParameter(event, kEventParamDirectObject, typeControlRef, sizeof(control), &control); |
} |
if (err == noErr) { |
err = SetEventParameter(event, kEventParamPostTarget, typeEventTargetRef, sizeof(target), &target); |
} |
if (err == noErr) { |
err = PostEventToQueue(GetCurrentEventQueue(), event, kEventPriorityStandard); |
} |
if (event != NULL) { |
ReleaseEvent(event); |
} |
return err; |
} |
static ControlKeyFilterUPP gControlEditTextModifiedKeyFilterUPP; // -> ControlEditTextModifiedKeyFilter |
extern pascal ControlKeyFilterUPP GetControlEditTextModifiedKeyFilterUPP(void) |
{ |
if (gControlEditTextModifiedKeyFilterUPP == NULL) { |
gControlEditTextModifiedKeyFilterUPP = NewControlKeyFilterUPP(ControlEditTextModifiedKeyFilter); |
} |
return gControlEditTextModifiedKeyFilterUPP; |
} |
extern pascal ControlKeyFilterResult ControlEditTextModifiedKeyFilter(ControlRef theControl, SInt16 *keyCode, SInt16 *charCode, EventModifiers *modifiers) |
{ |
OSStatus junk; |
ControlKeyFilterResult result; |
result = CallNextControlKeyFilter(theControl, kMIBControlEditTextModifiedKeyFilterTag, keyCode, charCode, modifiers); |
if (result == kControlKeyFilterPassKey) { |
junk = PostControlEditTextModifiedEvent(theControl); |
assert(junk == noErr); |
} |
return result; |
} |
static ControlEditTextValidationUPP gControlEditTextModifiedValidationProcUPP; // -> ControlEditTextModifiedValidationProc |
extern pascal ControlEditTextValidationUPP GetControlEditTextModifiedValidationProcUPP(void) |
{ |
if (gControlEditTextModifiedValidationProcUPP == NULL) { |
gControlEditTextModifiedValidationProcUPP = NewControlEditTextValidationUPP(ControlEditTextModifiedValidationProc); |
} |
return gControlEditTextModifiedValidationProcUPP; |
} |
extern pascal void ControlEditTextModifiedValidationProc(ControlRef control) |
{ |
OSStatus junk; |
junk = PostControlEditTextModifiedEvent(control); |
assert(junk == noErr); |
} |
#endif |
extern pascal void SetControlActive(ControlRef control, Boolean active) |
{ |
assert(control != NULL); |
if (active) { |
ActivateControl(control); |
} else { |
DeactivateControl(control); |
} |
} |
extern pascal void SetControlVisible(ControlRef control, Boolean visible) |
{ |
assert(control != NULL); |
if (visible) { |
ShowControl(control); |
} else { |
HideControl(control); |
} |
} |
Copyright © 2003 Apple Computer, Inc. All Rights Reserved. Terms of Use | Privacy Policy | Updated: 2003-10-27