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.
YASTControl.c
/* |
File: YASTControl.c |
Description: |
Yet Another Scrolling Text (YAST) Control. |
Yast, it lets you edit Unicode text. |
Author: JM |
Copyright: © Copyright 2003 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): |
Fri, Jan 28, 2000 -- created |
Fri, Jan 14, 2003 -- carbon event based, removed wne codes |
Fri, Apr 17, 2003 -- added data accessors |
*/ |
#include "YASTControl.h" |
/* YASTControlVars is a structure used for storing the the YASTControl's |
internal variables and state information. A reference to this structure |
is maintained by the carbon event manager and accessed in the control's |
carbon event handler by way of the userData parameter. */ |
typedef struct YASTControlVars YASTControlVars; |
typedef YASTControlVars *YASTControlVarsPtr; |
struct YASTControlVars { |
/* OS records referenced */ |
TXNObject fTXNObject; /* the txn record */ |
TXNFrameID fTXNFrameID; /* the txn frame ID */ |
ControlRef fControl; /* handle to the user pane control */ |
WindowRef fWindow; /* window containing control */ |
CGrafPtr fGrafPtr; /* grafport where control is drawn */ |
/* flags */ |
Boolean fInFocus; /* true while the focus rect is drawn around the control */ |
Boolean fIsActive; /* true while the control is drawn in the active state */ |
Boolean fTXNObjectActive; /* reflects the activation state of the text edit record */ |
Boolean fTabMovesFocus; /* true if tab moves focus (default: true) */ |
Boolean fDrawFocusBox; /* true if focus is drawn (default: true) */ |
Boolean fFocusDrawState; /* true if focus is drawn (default: true) */ |
Boolean fIsReadOnly; /* true if control is read only (default: false) */ |
/* calculated locations */ |
Rect fRBounds; /* complete bounds of control */ |
Rect fRTextArea; /* where the fTXNObject lives */ |
Rect fRFocusOutline; /* where the focus rectangle is drawn */ |
Rect fRTextOutline; /* where the text box border is drawn */ |
RgnHandle fRTextOutlineRegion; /* fRTextOutline stored as a region handle */ |
/* event handler refs */ |
EventHandlerRef fControlEvents; /* handlers we install for this control */ |
EventHandlerRef fWindowEvents; /* handlers we install in the control's window */ |
}; |
/* SetTextActivation activates or deactivates the text edit record |
according to the value of setActive. This routine ensures the activation |
switching call is only performed once for any particular activation state. */ |
static void SetTextActivation(YASTControlVars *varsp, Boolean setActive) { |
OSStatus err; |
if (varsp->fTXNObjectActive != setActive) { |
varsp->fTXNObjectActive = setActive; |
err = TXNActivate(varsp->fTXNObject, varsp->fTXNFrameID, varsp->fTXNObjectActive); |
if (varsp->fInFocus) |
TXNFocus( varsp->fTXNObject, varsp->fTXNObjectActive); |
} |
} |
/* RedrawFocusOutline redraws the focus rectangle as appropriate |
for the current focus, activation, and drawing state. This routine |
ensures the focus rectangle drawing is only performed once in any |
particular focus state. */ |
static void RedrawFocusOutline(YASTControlVars *varsp) { |
if (varsp->fDrawFocusBox) { /* drawing is on */ |
if (varsp->fFocusDrawState != (varsp->fIsActive && varsp->fInFocus)) { /* state changed */ |
varsp->fFocusDrawState = (varsp->fIsActive && varsp->fInFocus); |
SetPort(varsp->fGrafPtr); |
DrawThemeFocusRect(&varsp->fRFocusOutline, varsp->fFocusDrawState); |
} |
} else if (varsp->fFocusDrawState) { /* was drawn, but drawing has been turned off */ |
varsp->fFocusDrawState = false; |
SetPort(varsp->fGrafPtr); |
DrawThemeFocusRect(&varsp->fRFocusOutline, false); |
} |
} |
/* YASTControlCalculateBounds is called to recalculate all of the internal rectangles |
in the YASTControl's internal structures. bounds is the control's rectangle, the |
coordinates calculated in this routine are used to place the TXNObject, draw the focus |
rectangle, and draw the text box outline. */ |
static void YASTControlCalculateBounds(YASTControlVars *varsp, Rect* bounds) { |
SetRect(&varsp->fRBounds, bounds->left, bounds->top, bounds->right, bounds->bottom); |
SetRect(&varsp->fRFocusOutline, bounds->left, bounds->top, bounds->right, bounds->bottom); |
SetRect(&varsp->fRTextOutline, bounds->left+1, bounds->top+1, bounds->right-1, bounds->bottom-1); |
SetRect(&varsp->fRTextArea, bounds->left+2, bounds->top+3, bounds->right-3, bounds->bottom-2); |
RectRgn(varsp->fRTextOutlineRegion, &varsp->fRTextOutline); |
} |
/* control events we handle. We use this array of EventTypeSpec |
records to attach a carbon event handler to the control's event |
handling target. */ |
static const EventTypeSpec gYASTControlEvents[] = { |
{ kEventClassMouse, kEventMouseDown }, |
{ kEventClassControl, kEventControlClick }, |
{ kEventClassCommand, kEventProcessCommand }, |
{ kEventClassCommand, kEventCommandUpdateStatus }, |
{ kEventClassTextInput, kEventUnicodeForKeyEvent }, |
{ kEventClassControl, kEventControlSetCursor }, |
{ kEventClassControl, kEventControlDispose }, |
{ kEventClassControl, kEventControlSetData }, |
{ kEventClassControl, kEventControlGetData }, |
{ kEventClassControl, kEventControlBoundsChanged }, |
{ kEventClassControl, kEventControlActivate }, |
{ kEventClassControl, kEventControlDeactivate }, |
{ kEventClassControl, kEventControlHitTest }, |
{ kEventClassControl, kEventControlDraw }, |
{ kEventClassControl, kEventControlSetCursor }, |
{ kEventClassControl, kEventControlSetFocusPart } |
}; |
/* window events we handle. We use this array of EventTypeSpec |
records to attach a carbon event handler to the control's window's |
event handling target. */ |
static const EventTypeSpec gYASTControlWindowEvents[] = { |
{ kEventClassWindow, kEventWindowCursorChange }, |
}; |
/* YASTControlSetData is dispatched from our control event handler. It is where |
we handle all calls to SetControlData directed to our control. */ |
static OSStatus YASTControlSetData( |
YASTControlVarsPtr varsp, |
ResType inTagName, |
void * inBuffer, |
Size inBufferSize) { |
OSStatus err, returnedResult; |
/* default result */ |
returnedResult = eventNotHandledErr; |
/* dispatch according to the tag */ |
switch (inTagName) { |
case kYASTControlAllTextTag: /* char* */ |
case kYASTControlSelectedTextTag: /* char* */ |
{ TXNOffset oStartOffset, oEndOffset; |
/* pick the range of chars we want to replace */ |
if (inTagName == kYASTControlSelectedTextTag) |
TXNGetSelection( varsp->fTXNObject, &oStartOffset, &oEndOffset); |
else { oStartOffset = kTXNStartOffset; oEndOffset = kTXNEndOffset; } |
/* get the new text */ |
err = TXNSetData( varsp->fTXNObject, kTXNTextData, |
inBuffer, inBufferSize, oStartOffset, oEndOffset ); |
} |
returnedResult = err; |
break; |
case kYASTControlAllUnicodeTextTag: /* CFStringRef */ |
case kYASTControlSelectedUnicodeTextTag: /* CFStringRef */ |
if (inBufferSize != sizeof(CFStringRef)) { |
err = paramErr; |
} else { |
CFStringRef theString; |
CFIndex length; |
UniChar *buffer; |
TXNOffset oStartOffset, oEndOffset; |
/* pick the range of chars we want to replace */ |
if (inTagName == kYASTControlSelectedUnicodeTextTag) |
TXNGetSelection( varsp->fTXNObject, &oStartOffset, &oEndOffset); |
else { oStartOffset = kTXNStartOffset; oEndOffset = kTXNEndOffset; } |
theString = * (CFStringRef*) inBuffer; |
/* get the new text */ |
length = CFStringGetLength(theString); |
buffer = (UniChar *) malloc(length*sizeof(UniChar)); |
if (buffer != NULL) { |
CFRange range; |
range.location = 0; |
range.length = length; |
CFStringGetCharacters(theString, range, buffer); |
/* add the new text */ |
err = TXNSetData( varsp->fTXNObject, kTXNUnicodeTextData, |
buffer, length*2, oStartOffset, oEndOffset); |
free(buffer); |
} else { |
err = memFullErr; |
} |
} |
returnedResult = err; |
break; |
case kYASTControlSelectionRangeTag: /* YASTControlEditTextSelectionRec */ |
if (inBufferSize != sizeof(CFRange)) { |
err = paramErr; |
} else { |
YASTControlEditTextSelectionPtr range; |
range = (YASTControlEditTextSelectionPtr) inBuffer; |
err = TXNSetSelection( varsp->fTXNObject, range->selStart, range->selEnd); |
if (err == noErr) { |
TXNShowSelection( varsp->fTXNObject, false); |
} |
} |
returnedResult = err; |
break; |
case kYASTControlTabsAdvanceFocusTag: /* Boolean (default true) */ |
if (inBufferSize != sizeof(Boolean)) { |
err = paramErr; |
} else { |
varsp->fTabMovesFocus = * (Boolean*) inBuffer; |
err = noErr; |
} |
returnedResult = err; |
break; |
case kYASTControlDoDrawFocusTag: /* Boolean (default true) */ |
if (inBufferSize != sizeof(Boolean)) { |
err = paramErr; |
} else { |
varsp->fDrawFocusBox = * (Boolean*) inBuffer; |
RedrawFocusOutline(varsp); |
err = noErr; |
} |
returnedResult = err; |
break; |
case kYASTControlReadOnlyTag: |
if (inBufferSize != sizeof(Boolean)) { |
err = paramErr; |
} else { |
TXNControlData txnCControlData; |
TXNControlTag txnControlTag; |
txnControlTag = kTXNIOPrivilegesTag; |
if ( * (Boolean*) inBuffer ) |
txnCControlData.uValue = kTXNReadOnly; |
else txnCControlData.uValue = kTXNReadWrite; |
err = TXNSetTXNObjectControls( varsp->fTXNObject, |
false, 1, &txnControlTag, &txnCControlData ); |
if (err == noErr) { |
varsp->fIsReadOnly = * (Boolean*) inBuffer; |
} |
} |
returnedResult = err; |
break; |
case kYASTControlTabSizeTag: |
if (inBufferSize != sizeof(SInt16)) { |
err = paramErr; |
} else { |
TXNControlData txnTabData; |
TXNControlTag txnControlTag; |
txnControlTag = kTXNTabSettingsTag; |
txnTabData.tabValue.value = * (SInt16*) inBuffer; |
txnTabData.tabValue.tabType = kTXNRightTab; |
txnTabData.tabValue.filler = 0; |
err = TXNSetTXNObjectControls( varsp->fTXNObject, |
false, 1, &txnControlTag, &txnTabData ); |
} |
returnedResult = err; |
break; |
} |
return returnedResult; |
} |
/* YASTControlGetData is dispatched from our control event handler. It is where |
we handle all calls to GetControlData directed to our control. */ |
static OSStatus YASTControlGetData( |
YASTControlVarsPtr varsp, |
ResType inTagName, |
void * inBuffer, |
Size inBufferSize, |
Size *outBufferSize) { |
OSStatus err, returnedResult; |
/* default result */ |
returnedResult = eventNotHandledErr; |
/* dispatch event */ |
switch (inTagName) { |
case kYASTControlAllTextTag: |
case kYASTControlSelectedTextTag: |
{ Handle oDataHandle; |
Size bytesCopied; |
TXNOffset oStartOffset, oEndOffset; |
if (inTagName == kYASTControlSelectedTextTag) |
TXNGetSelection( varsp->fTXNObject, &oStartOffset, &oEndOffset); |
else { oStartOffset = kTXNStartOffset; oEndOffset = kTXNEndOffset; } |
err = TXNGetDataEncoded( varsp->fTXNObject, oStartOffset, |
oEndOffset, &oDataHandle, kTXNTextData); |
if (err == noErr) { |
bytesCopied = GetHandleSize(oDataHandle); |
if (bytesCopied > inBufferSize) bytesCopied = inBufferSize; |
BlockMoveData(*oDataHandle, inBuffer, bytesCopied); |
if (outBufferSize != NULL) *outBufferSize = bytesCopied; |
DisposeHandle(oDataHandle); |
} |
} |
returnedResult = err; |
break; |
case kYASTControlAllUnicodeTextTag: /* CFStringRef */ |
case kYASTControlSelectedUnicodeTextTag: /* CFStringRef */ |
if (inBufferSize != sizeof(CFStringRef)) { |
err = paramErr; |
} else { |
TXNOffset oStartOffset, oEndOffset; |
Handle oDataHandle; |
if (inTagName == kYASTControlSelectedUnicodeTextTag) |
TXNGetSelection( varsp->fTXNObject, &oStartOffset, &oEndOffset); |
else { oStartOffset = kTXNStartOffset; oEndOffset = kTXNEndOffset; } |
err = TXNGetDataEncoded( varsp->fTXNObject, oStartOffset, |
oEndOffset, &oDataHandle, kTXNUnicodeTextData); |
if (err == noErr) { |
CFStringRef theString; |
HLock(oDataHandle); |
theString = CFStringCreateWithCharacters(NULL, |
(UniChar *) (*oDataHandle), GetHandleSize(oDataHandle)/sizeof(UniChar)); |
if (theString != NULL) { |
* (CFStringRef*) inBuffer = theString; |
if (outBufferSize != NULL) *outBufferSize = sizeof(CFStringRef); |
err = noErr; |
} else { |
err = memFullErr; |
} |
DisposeHandle(oDataHandle); |
} |
} |
returnedResult = err; |
break; |
case kYASTControlSelectionRangeTag: /* YASTControlEditTextSelectionRec */ |
if (inBufferSize != sizeof(YASTControlEditTextSelectionRec)) { |
err = paramErr; |
} else { |
YASTControlEditTextSelectionPtr range; |
range = (YASTControlEditTextSelectionPtr) inBuffer; |
TXNGetSelection( varsp->fTXNObject, &range->selStart, &range->selEnd); |
if (outBufferSize != NULL) *outBufferSize = sizeof(YASTControlEditTextSelectionRec); |
err = noErr; |
} |
returnedResult = err; |
break; |
case kYASTControlTXNObjectTag: /* fTXNObject - GetControlData only */ |
if (inBufferSize != sizeof(TXNObject)) { |
err = paramErr; |
} else { |
* (TXNObject*) inBuffer = varsp->fTXNObject; |
if (outBufferSize != NULL) *outBufferSize = sizeof(TXNObject); |
err = noErr; |
} |
returnedResult = err; |
break; |
case kYASTControlTabsAdvanceFocusTag: /* Boolean (default true) */ |
if (inBufferSize != sizeof(Boolean)) { |
err = paramErr; |
} else { |
* (Boolean*) inBuffer = varsp->fTabMovesFocus; |
if (outBufferSize != NULL) *outBufferSize = sizeof(Boolean); |
err = noErr; |
} |
returnedResult = err; |
break; |
case kYASTControlDoDrawFocusTag: /* Boolean (default true) */ |
if (inBufferSize != sizeof(Boolean)) { |
err = paramErr; |
} else { |
* (Boolean*) inBuffer = varsp->fDrawFocusBox; |
if (outBufferSize != NULL) *outBufferSize = sizeof(Boolean); |
err = noErr; |
} |
returnedResult = err; |
break; |
case kYASTControlReadOnlyTag: |
if (inBufferSize != sizeof(Boolean)) { |
err = paramErr; |
} else { |
* (Boolean*) inBuffer = varsp->fIsReadOnly; |
if (outBufferSize != NULL) *outBufferSize = sizeof(Boolean); |
err = noErr; |
} |
returnedResult = err; |
break; |
case kYASTControlTabSizeTag: |
if (inBufferSize != sizeof(SInt16)) { |
err = paramErr; |
} else { |
TXNControlData txnTabData; |
TXNControlTag txnControlTag; |
txnControlTag = kTXNTabSettingsTag; |
err = TXNGetTXNObjectControls( varsp->fTXNObject, 1, &txnControlTag, &txnTabData ); |
if (err == noErr) { |
* (SInt16*) inBuffer = txnTabData.tabValue.value; |
if (outBufferSize != NULL) *outBufferSize = sizeof(SInt16); |
} |
} |
returnedResult = err; |
break; |
} |
return returnedResult; |
} |
/* YASTControlCarbonEventHandler defines the main entry point for all |
of the carbon event handlers installed for the YASTControl. */ |
static pascal OSStatus YASTControlCarbonEventHandler( |
EventHandlerCallRef myHandler, |
EventRef event, |
void* userData) { |
#pragma unused ( myHandler ) |
OSStatus err, returnedResult; |
YASTControlVarsPtr varsp; |
UInt32 eclass, ekind; |
/* set up locals */ |
eclass = GetEventClass(event); |
ekind = GetEventKind(event); |
varsp = (YASTControlVarsPtr) userData; |
returnedResult = eventNotHandledErr; |
/* dispatch the event by class*/ |
switch (eclass) { |
case kEventClassWindow: |
if ( ekind == kEventWindowCursorChange ) { |
Point where; |
UInt32 modifiers; |
Boolean cursorWasSet; |
/* get the mouse position */ |
err = GetEventParameter( event, kEventParamMouseLocation, |
typeQDPoint, NULL, sizeof(where), NULL, &where); |
if (err == noErr) { |
err = GetEventParameter( event, kEventParamKeyModifiers, |
typeUInt32, NULL, sizeof(modifiers), NULL, &modifiers); |
if (err == noErr) { |
SetPort(varsp->fGrafPtr); |
GlobalToLocal(&where); |
if (PtInRect(where, &varsp->fRBounds)) { |
err = HandleControlSetCursor( varsp->fControl, where, modifiers, &cursorWasSet); |
if (err != noErr) cursorWasSet = false; |
if ( ! cursorWasSet ) InitCursor(); |
returnedResult = noErr; |
} |
} |
} |
} |
break; |
case kEventClassMouse: |
/* handle mouse downs in the control, but only if the |
control is in focus. */ |
if ( ekind == kEventMouseDown ) { |
EventRecord outEvent; |
if ( varsp->fInFocus ) { |
if (ConvertEventRefToEventRecord( event, &outEvent)) { |
TXNClick( varsp->fTXNObject, &outEvent); |
} |
returnedResult = noErr; |
} |
} |
break; |
case kEventClassTextInput: |
if ( ekind == kEventUnicodeForKeyEvent |
&& varsp->fTabMovesFocus) { |
UniChar mUnicodeText[8]; |
UInt32 bytecount, nchars; |
/* get the character */ |
err = GetEventParameter(event, kEventParamTextInputSendText, |
typeUnicodeText, NULL, sizeof(mUnicodeText), |
&bytecount, (char*) mUnicodeText); |
if ((err == noErr) |
&& (bytecount >= sizeof(UniChar))) { |
nchars = ( bytecount / sizeof(UniChar) ); |
/* if it's not the tab key, forget it... */ |
if ( mUnicodeText[0] == '\t' ) { |
EventRef rawKeyEvent; |
Boolean shiftDown; |
/* is the shift key held down? */ |
shiftDown = false; |
err = GetEventParameter(event, kEventParamTextInputSendKeyboardEvent, |
typeEventRef, NULL, sizeof(rawKeyEvent), NULL, &rawKeyEvent); |
if (err == noErr) { |
UInt32 modifiers; |
err = GetEventParameter(rawKeyEvent, kEventParamKeyModifiers, |
typeUInt32, NULL, sizeof(modifiers), NULL, &modifiers); |
if (err == noErr) { |
shiftDown = ( (modifiers & shiftKey) != 0 ); |
} |
} |
/* advance the keyboard focus, backwards if shift is down */ |
if (shiftDown) |
ReverseKeyboardFocus( varsp->fWindow ); |
else AdvanceKeyboardFocus( varsp->fWindow ); |
/* noErr lets the CEM know we handled the event */ |
returnedResult = noErr; |
} |
} |
} |
break; |
case kEventClassControl: |
switch (ekind) { |
case kEventControlSetFocusPart: |
{ ControlPartCode thePart; |
err = GetEventParameter(event, kEventParamControlPart, |
typeControlPartCode, NULL, sizeof(thePart), NULL, &thePart); |
if (err == noErr) { |
switch (thePart) { |
default: |
case kControlFocusNoPart: /* turn off focus */ |
if ( varsp->fInFocus ) { |
TXNFocus( varsp->fTXNObject, false); |
varsp->fInFocus = false; |
} |
thePart = kControlFocusNoPart; |
break; |
case kYASTControlOnlyPart: /* turn on focus */ |
if ( ! varsp->fInFocus ) { |
TXNFocus( varsp->fTXNObject, true); |
varsp->fInFocus = true; |
} |
thePart = kYASTControlOnlyPart; |
break; |
case kControlFocusPrevPart: /* toggle focus on/off */ |
case kControlFocusNextPart: |
varsp->fInFocus = ! varsp->fInFocus; |
TXNFocus( varsp->fTXNObject, varsp->fInFocus); |
thePart = (varsp->fInFocus ? kYASTControlOnlyPart : kControlFocusNoPart); |
break; |
} |
SetPort(varsp->fGrafPtr); |
/* calculate the next highlight state */ |
SetTextActivation(varsp, varsp->fIsActive && varsp->fInFocus); |
/* redraw the text fram and focus rectangle to indicate the |
new focus state */ |
DrawThemeEditTextFrame(&varsp->fRTextOutline, |
varsp->fIsActive ? kThemeStateActive: kThemeStateInactive); |
RedrawFocusOutline(varsp); |
} |
/* pass back the foocus part code */ |
err = SetEventParameter( event, kEventParamControlPart, |
typeControlPartCode, sizeof(thePart), &thePart); |
returnedResult = err; |
} |
break; |
case kEventControlHitTest: |
/* this event does not necessairly mean that a mouse click |
has occured. Here we are simply testing to see if a particular |
point is located inside of the control. More complicated controls |
would return different part codes for different parts of |
themselves; but, since YASTControls only advertise one part, the |
hit test here is more or less a boolean test. */ |
{ ControlPartCode thePart; |
Point where; |
err = GetEventParameter(event, kEventParamMouseLocation, |
typeQDPoint, NULL, sizeof(where), NULL, &where); |
if (err == noErr) { |
if (PtInRect(where, &varsp->fRTextArea)) { |
thePart = kYASTControlOnlyPart; |
} else thePart = 0; |
err = SetEventParameter( event, kEventParamControlPart, |
typeControlPartCode, sizeof(thePart), &thePart); |
} |
returnedResult = err; |
} |
break; |
case kEventControlClick: |
/* here we handle focus switching on the control. Actual tracking |
of mouse down events in the control is performed in the kEventClassMouse |
mouse down handler above. */ |
if ( ! varsp->fInFocus ) { |
SetKeyboardFocus(varsp->fWindow, varsp->fControl, kYASTControlOnlyPart); |
returnedResult = noErr; |
} |
break; |
case kEventControlBoundsChanged: |
/* we moved, or switched size - recalculate our rectangles */ |
{ Rect bounds; |
err = GetEventParameter(event, kEventParamCurrentBounds, |
typeQDRectangle, NULL, sizeof(bounds), NULL, &bounds); |
if (err == noErr) { |
YASTControlCalculateBounds(varsp, &bounds); |
TXNSetFrameBounds( varsp->fTXNObject, |
varsp->fRTextArea.top, varsp->fRTextArea.left, |
varsp->fRTextArea.bottom, varsp->fRTextArea.right, |
varsp->fTXNFrameID); |
} |
} |
break; |
case kEventControlActivate: |
case kEventControlDeactivate: |
{ SetPort(varsp->fGrafPtr); |
varsp->fIsActive = (ekind == kEventControlActivate); |
SetTextActivation(varsp, varsp->fIsActive && varsp->fInFocus); |
/* redraw the frame */ |
DrawThemeEditTextFrame(&varsp->fRTextOutline, |
varsp->fIsActive ? kThemeStateActive: kThemeStateInactive); |
RedrawFocusOutline(varsp); |
returnedResult = noErr; |
} |
break; |
case kEventControlDraw: |
/* redraw the control */ |
SetPort(varsp->fGrafPtr); |
/* update the text region */ |
TXNDraw(varsp->fTXNObject, NULL); |
/* restore the drawing environment */ |
/* draw the text frame and focus frame (if necessary) */ |
DrawThemeEditTextFrame(&varsp->fRTextOutline, |
varsp->fIsActive ? kThemeStateActive: kThemeStateInactive); |
RedrawFocusOutline(varsp); |
returnedResult = noErr; |
break; |
case kEventControlSetCursor: |
/* cursor adjustment */ |
{ SetPortWindowPort(varsp->fWindow); |
TXNAdjustCursor( varsp->fTXNObject, varsp->fRTextOutlineRegion); |
returnedResult = noErr; |
} |
break; |
case kEventControlDispose: |
/* RemoveEventHandler(varsp->fControlEvents); -- this call has been |
left out on purpose because it will be called automatically when the |
control is disposed. */ |
RemoveEventHandler(varsp->fWindowEvents); |
TXNDeleteObject(varsp->fTXNObject); |
DisposeRgn(varsp->fRTextOutlineRegion); |
free(varsp); |
/* returnedResult = noErr; -- this has been left out on purpose |
because we want the dispatching to continue and dispose of the control */ |
break; |
case kEventControlSetData: |
{ ResType inTagName; |
Size inBufferSize; |
void * inBuffer; |
err = GetEventParameter( event, kEventParamControlDataTag, typeEnumeration, |
NULL, sizeof(inTagName), NULL, &inTagName); |
if (err == noErr) { |
err = GetEventParameter( event, kEventParamControlDataBuffer, typePtr, |
NULL, sizeof(inBuffer), NULL, &inBuffer); |
if (err == noErr) { |
err = GetEventParameter( event, kEventParamControlDataBufferSize, typeLongInteger, |
NULL, sizeof(inBufferSize), NULL, &inBufferSize); |
if (err == noErr) { |
err = YASTControlSetData(varsp, inTagName, inBuffer, inBufferSize); |
} |
} |
} |
returnedResult = err; |
} |
break; |
case kEventControlGetData: |
{ ResType inTagName; |
Size inBufferSize, outBufferSize; |
void * inBuffer; |
err = GetEventParameter( event, kEventParamControlDataTag, typeEnumeration, |
NULL, sizeof(inTagName), NULL, &inTagName); |
if (err == noErr) { |
err = GetEventParameter( event, kEventParamControlDataBuffer, typePtr, |
NULL, sizeof(inBuffer), NULL, &inBuffer); |
if (err == noErr) { |
err = GetEventParameter( event, kEventParamControlDataBufferSize, typeLongInteger, |
NULL, sizeof(inBufferSize), NULL, &inBufferSize); |
if (err == noErr) { |
err = YASTControlGetData(varsp, inTagName, inBuffer, inBufferSize, &outBufferSize); |
if (err == noErr) { |
err = SetEventParameter( event, kEventParamControlDataBufferSize, |
typeLongInteger, sizeof(outBufferSize), &outBufferSize); |
} |
} |
} |
} |
returnedResult = err; |
} |
break; |
} |
break; |
case kEventClassCommand: |
if ( ekind == kEventProcessCommand ) { |
HICommand command; |
err = GetEventParameter( event, kEventParamDirectObject, |
typeHICommand, NULL, sizeof(command), NULL, &command); |
if (err == noErr) { |
switch (command.commandID) { |
case kHICommandUndo: |
TXNUndo(varsp->fTXNObject); |
returnedResult = noErr; |
break; |
case kHICommandRedo: |
TXNRedo(varsp->fTXNObject); |
returnedResult = noErr; |
break; |
case kHICommandCut: |
ClearCurrentScrap(); |
err = TXNCut(varsp->fTXNObject); |
if (err == noErr) |
err = TXNConvertToPublicScrap(); |
returnedResult = err; |
break; |
case kHICommandCopy: |
ClearCurrentScrap(); |
err = TXNCopy(varsp->fTXNObject); |
if (err == noErr) |
err = TXNConvertToPublicScrap(); |
returnedResult = err; |
break; |
case kHICommandPaste: |
err = TXNConvertFromPublicScrap(); |
if (err == noErr) |
err = TXNPaste(varsp->fTXNObject); |
returnedResult = err; |
break; |
case kHICommandClear: |
err = TXNClear(varsp->fTXNObject); |
returnedResult = err; |
break; |
case kHICommandSelectAll: |
err = TXNSetSelection(varsp->fTXNObject, kTXNStartOffset, kTXNEndOffset); |
returnedResult = err; |
break; |
} |
} |
} else if ( ekind == kEventCommandUpdateStatus ) { |
HICommand command; |
TXNOffset oStartOffset, oEndOffset; |
TXNActionKey oActionKey; |
err = GetEventParameter( event, kEventParamDirectObject, typeHICommand, |
NULL, sizeof(command), NULL, &command); |
if ((err == noErr) |
&& ((command.attributes & kHICommandFromMenu) != 0)) { |
switch (command.commandID) { |
case kHICommandUndo: |
if (TXNCanUndo(varsp->fTXNObject, &oActionKey)) { |
EnableMenuItem(command.menu.menuRef, 0); /* required pre OS 10.2 */ |
EnableMenuItem(command.menu.menuRef, command.menu.menuItemIndex); |
} else DisableMenuItem(command.menu.menuRef, command.menu.menuItemIndex); |
returnedResult = noErr; |
break; |
case kHICommandRedo: |
if (TXNCanRedo(varsp->fTXNObject, &oActionKey)) { |
EnableMenuItem(command.menu.menuRef, 0); /* required pre OS 10.2 */ |
EnableMenuItem(command.menu.menuRef, command.menu.menuItemIndex); |
} else DisableMenuItem(command.menu.menuRef, command.menu.menuItemIndex); |
returnedResult = noErr; |
break; |
case kHICommandCut: |
case kHICommandCopy: |
case kHICommandClear: |
TXNGetSelection(varsp->fTXNObject, &oStartOffset, &oEndOffset); |
if (oStartOffset != oEndOffset) { |
EnableMenuItem(command.menu.menuRef, 0); /* required pre OS 10.2 */ |
EnableMenuItem(command.menu.menuRef, command.menu.menuItemIndex); |
} else DisableMenuItem(command.menu.menuRef, command.menu.menuItemIndex); |
returnedResult = noErr; |
break; |
case kHICommandPaste: |
if (TXNIsScrapPastable()) { |
EnableMenuItem(command.menu.menuRef, 0); /* required pre OS 10.2 */ |
EnableMenuItem(command.menu.menuRef, command.menu.menuItemIndex); |
} else DisableMenuItem(command.menu.menuRef, command.menu.menuItemIndex); |
returnedResult = noErr; |
break; |
case kHICommandSelectAll: |
if(TXNDataSize(varsp->fTXNObject) > 0) { |
EnableMenuItem(command.menu.menuRef, 0); /* required pre OS 10.2 */ |
EnableMenuItem(command.menu.menuRef, command.menu.menuItemIndex); |
} else DisableMenuItem(command.menu.menuRef, command.menu.menuItemIndex); |
returnedResult = noErr; |
break; |
} |
} |
} |
break; |
} |
return returnedResult; |
} |
/* YASTControlAttachToExistingControl initializes Yet Another |
Scrolling Text (YAST) control on top of an existing control - preferably |
a user pane control created in interface builder. Once set up, carbon |
event handlers installed by this routine take care of everything else, |
including any necessary internal storage cleanup operations when the |
control is disposed. */ |
OSStatus YASTControlAttachToExistingControl(ControlRef theControl) { |
OSStatus err; |
YASTControlVars *varsp; |
UInt32 outCommandID; |
EventHandlerRef controlEvents, windowEvents; |
TXNObject theTXNObject; |
RgnHandle outlineRegion; |
/* set up our locals */ |
controlEvents = windowEvents = NULL; |
theTXNObject = NULL; |
outlineRegion = NULL; |
varsp = NULL; |
err = noErr; |
/* allocate our private storage and set up initial settings*/ |
varsp = (YASTControlVars *) malloc(sizeof(YASTControlVars)); |
if (varsp == NULL) { |
err = memFullErr; |
} else { |
varsp->fInFocus = false; |
varsp->fIsActive = true; |
varsp->fTXNObjectActive = false; |
varsp->fControl = theControl; |
varsp->fTabMovesFocus = true; |
varsp->fDrawFocusBox = true; |
varsp->fFocusDrawState = false; |
varsp->fIsReadOnly = false; |
varsp->fRTextOutlineRegion = NULL; |
varsp->fWindow = GetControlOwner(theControl); |
varsp->fGrafPtr = GetWindowPort(varsp->fWindow); |
} |
/* set our control's command id. we don't actually use it, but it must |
be non-zero for our control to be sent command events. only set it |
if it has not already been set. */ |
err = GetControlCommandID(theControl, &outCommandID); |
if (err == noErr) { |
if (outCommandID == 0) { |
err = SetControlCommandID(theControl, 1); |
} |
} |
/* calculate the rectangles used by the control */ |
if (err == noErr) { |
outlineRegion = NewRgn(); |
if (outlineRegion == NULL) { |
err = memFullErr; |
} else { |
Rect bounds; |
varsp->fRTextOutlineRegion = outlineRegion; |
GetControlBounds(theControl, &bounds); |
YASTControlCalculateBounds(varsp, &bounds); |
} |
} |
/* create the new edit field */ |
if (err == noErr) { |
err = TXNNewObject(NULL, varsp->fWindow, &varsp->fRTextArea, |
kTXNWantVScrollBarMask | kTXNAlwaysWrapAtViewEdgeMask, |
kTXNTextEditStyleFrameType, kTXNTextensionFile, kTXNSystemDefaultEncoding, |
&theTXNObject, &varsp->fTXNFrameID, (TXNObjectRefcon) varsp); |
if (err == noErr) { |
varsp->fTXNObject = theTXNObject; |
} |
} |
/* set the field's background */ |
if (err == noErr) { |
RGBColor rgbWhite = {0xFFFF, 0xFFFF, 0xFFFF}; |
TXNBackground tback; |
tback.bgType = kTXNBackgroundTypeRGB; |
tback.bg.color = rgbWhite; |
TXNSetBackground( varsp->fTXNObject, &tback); |
} |
/* set the margins for easier selection and display */ |
if (err == noErr) { |
TXNControlData txnCControlData; |
TXNControlTag txnControlTag = kTXNMarginsTag; |
TXNMargins txnMargins = { 2, 3, 2, 1 }; /* t,l,b,r */ |
txnCControlData.marginsPtr = &txnMargins; |
(void) TXNSetTXNObjectControls( varsp->fTXNObject, false, 1, &txnControlTag, &txnCControlData ); |
} |
/* install our carbon event handlers */ |
if (err == noErr) { |
static EventHandlerUPP gTPEventHandlerUPP = NULL; |
if (gTPEventHandlerUPP == NULL) |
gTPEventHandlerUPP = NewEventHandlerUPP(YASTControlCarbonEventHandler); |
/* carbon event handlers for the control */ |
err = InstallEventHandler( GetControlEventTarget( theControl ), |
gTPEventHandlerUPP, |
(sizeof(gYASTControlEvents)/sizeof(EventTypeSpec)), |
gYASTControlEvents, |
varsp, &controlEvents); |
if (err == noErr) { |
varsp->fControlEvents = windowEvents; |
/* carbon event handlers for the control's window */ |
err = InstallEventHandler( GetWindowEventTarget( varsp->fWindow ), |
gTPEventHandlerUPP, (sizeof(gYASTControlWindowEvents)/sizeof(EventTypeSpec)), |
gYASTControlWindowEvents, varsp, &windowEvents); |
if (err == noErr) { |
varsp->fWindowEvents = windowEvents; |
} |
} |
} |
/* perform final activations and setup for our text field. Here, |
we assume that the window is going to be the 'active' window. */ |
if (err == noErr) { |
SetTextActivation(varsp, (varsp->fIsActive && varsp->fInFocus)); |
} |
/* clean up on error */ |
if (err != noErr) { |
if (controlEvents != NULL) RemoveEventHandler(controlEvents); |
if (windowEvents != NULL) RemoveEventHandler(windowEvents); |
if (theTXNObject != NULL) TXNDeleteObject(theTXNObject); |
if (outlineRegion != NULL) DisposeRgn(outlineRegion); |
if (varsp != NULL) free((void*) varsp); |
} |
/* all done */ |
return err; |
} |
/* CreateYASTControl creates Yet Another Scrolling Text (YAST) control for |
use in theWindow. Specifically, this routine creates a new user pane control |
and then calls YASTControlAttachToExistingControl to finish the job. */ |
OSStatus CreateYASTControl(WindowRef theWindow, Rect *bounds, ControlRef *theControl) { |
UInt32 featurSet; |
ControlRef theNewControl; |
OSStatus err; |
/* feature flags for our control. */ |
featurSet = kControlSupportsEmbedding | kControlSupportsFocus | kControlWantsIdle |
| kControlWantsActivate | kControlHasSpecialBackground |
| kControlGetsFocusOnClick | kControlSupportsLiveFeedback; |
/* create the control */ |
err = CreateUserPaneControl( theWindow, bounds, featurSet, &theNewControl); |
if (err == noErr) { |
/* set up the txn features */ |
err = YASTControlAttachToExistingControl(theNewControl); |
if (err == noErr) { |
*theControl = theNewControl; |
} else { |
DisposeControl(theNewControl); |
} |
} |
/* all done.... */ |
return err; |
} |
Copyright © 2003 Apple Computer, Inc. All Rights Reserved. Terms of Use | Privacy Policy | Updated: 2003-10-27