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.
Source/SVDrag.c
/* |
File: SVDrag.c |
Contains: |
Written by: Original version by Jon Lansdell and Nigel Humphreys. |
3.1 updates by Greg Sutton. |
Copyright: Copyright ©1995-1999 by Apple Computer, Inc., All Rights Reserved. |
You may incorporate this Apple sample source code into your program(s) without |
restriction. This Apple sample source code has been provided "AS IS" and the |
responsibility for its operation is yours. You are not permitted to redistribute |
this Apple sample source code as "Apple sample source code" after having made |
changes. If you're going to re-distribute the source, we require that you make |
it clear in the source that the code was descended from Apple sample source |
code, but that you've made changes. |
Change History (most recent first): |
7/19/1999 Karl Groethe Updated for Metrowerks Codewarror Pro 2.1 |
*/ |
#include "SVDrag.h" |
#include "SVEditWindow.h" |
#include "SVEditGlobals.h" |
#include "Offscreen.h" |
#include "DebugUtils.h" |
#include <Drag.h> |
#include <LowMem.h> |
#include <Errors.h> |
#include <Folders.h> |
static short gCaretOffset; // Caret drawn during a drag |
#pragma segment Drag |
// |
// InitDragHandlers creates the UPPs for the Drag Manager |
// callback routines _if_ the Drag Manager is available. |
// |
OSErr InitDragHandlers ( void ) |
{ |
OSErr theErr = noErr; |
if ( gHasDragManager ) |
{ |
gDragTrackingHandlerUPP = NewDragTrackingHandlerProc ( MyTrackingHandler ); |
gDragReceiveHandlerUPP = NewDragReceiveHandlerProc ( MyReceiveHandler ); |
} |
return theErr; |
} |
// |
// InstallDragHandlers attaches the tracking and receive handlers to |
// one of the application's windows. |
// |
OSErr InstallDragHandlers ( WindowRef theWindow ) |
{ |
OSErr theErr = noErr; |
if ( gHasDragManager ) |
{ |
theErr = InstallTrackingHandler ( gDragTrackingHandlerUPP, theWindow, nil ); |
if ( theErr == noErr ) |
{ |
theErr = InstallReceiveHandler ( gDragReceiveHandlerUPP, theWindow, nil ); |
if ( theErr ) |
(void) RemoveTrackingHandler ( gDragTrackingHandlerUPP, theWindow ); |
} |
} |
return theErr; |
} |
// |
// RemoveDragHandlers removes the tracking and receive handlers from |
// one of the application's windows (usually just prior to disposal). |
// |
void RemoveDragHandlers ( WindowRef theWindow ) |
{ |
if ( gHasDragManager ) |
{ |
RemoveReceiveHandler ( gDragReceiveHandlerUPP, theWindow ); |
RemoveTrackingHandler ( gDragTrackingHandlerUPP, theWindow ); |
} |
return; |
} |
Boolean IsDragInWindowContent ( DragReference theDrag, WindowRef theWindow ) |
{ |
Point localPt; |
GetDragMouse ( theDrag, &localPt, 0L ); |
GlobalToLocal ( &localPt ); // Assumes theWindow is current port |
return PtInWindow ( localPt, theWindow ); |
} |
Boolean PtInWindow ( Point localPt, WindowRef theWindow ) |
{ |
DPtr theDocument; |
theDocument = DPtrFromWindowPtr ( theWindow ); |
return PtInDocument ( localPt, theDocument ); |
} |
Boolean PtInDocument ( Point localPt, DPtr theDocument ) |
{ |
return PtInRect ( localPt, &(**(theDocument->theText)).viewRect ); |
} |
Boolean CanAcceptDragItems ( DragReference theDrag ) |
{ |
OSErr theErr; |
ItemReference theRef; |
Boolean bCanAccept = false; |
HFSFlavor currHFSFlavor; |
Size flavorDataSize; |
FlavorFlags currFlavorFlags; |
theErr = GetDragItemReferenceNumber ( theDrag, 1, &theRef ); |
if ( theErr == noErr ) |
{ |
// use GetFlavorFlags to check on flavor existence of TEXT data. |
theErr = GetFlavorFlags ( theDrag, theRef, 'TEXT', &currFlavorFlags ); |
if ( theErr == noErr ) |
bCanAccept = true; |
else |
{ |
// check if the item is a file spec, and it contains TEXT |
flavorDataSize = sizeof ( HFSFlavor ); |
theErr = GetFlavorData ( theDrag, theRef, flavorTypeHFS, &currHFSFlavor, |
&flavorDataSize, 0 ); |
if ( theErr == noErr && currHFSFlavor.fileType == 'TEXT' ) |
bCanAccept = true; |
} |
} |
return bCanAccept; |
} |
pascal OSErr MyTrackingHandler ( DragTrackingMessage theMessage, WindowPtr theWindow, |
void* handlerRefCon, DragReference theDrag ) |
{ |
#pragma unused(handlerRefCon) |
static Boolean bHasAcceptableDrag; |
static Boolean bHasHilitedWindow; |
static Boolean bShowCaret; |
static long caretMovedTime; |
static short lastOffset, insertPosition; |
short theOffset; |
OSErr theErr = noErr; |
long theTime = TickCount ( ); |
unsigned long theAttributes; |
RgnHandle tempRgn; |
DPtr theDocument, hitDocument; |
GetDragAttributes ( theDrag, &theAttributes ); |
theDocument = DPtrFromWindowPtr ( theWindow ); |
switch ( theMessage ) |
{ |
case kDragTrackingEnterHandler: |
// Any initialization for this window handler. |
bHasAcceptableDrag = CanAcceptDragItems ( theDrag ); |
// Let the drag manager know if we can't accept this drag |
if ( !bHasAcceptableDrag ) |
theErr = dragNotAcceptedErr; |
break; |
case kDragTrackingEnterWindow: |
caretMovedTime = theTime; |
gCaretOffset = lastOffset = -1; |
bShowCaret = true; |
bHasHilitedWindow = false; |
break; |
case kDragTrackingInWindow: |
// Hiliting of the window during a drag is done |
// here. Do it only if we can accept these items |
// and we're not in the source window. |
if ( bHasAcceptableDrag ) |
{ |
// Check if the mouse is in a window's content region. Make an |
// exception if the window has yet to leave the source window, |
// since we don't want to hilite it in that stuation. |
Boolean bMouseInContent; |
Point theMouse; |
bMouseInContent = false; |
if ( theAttributes & kDragHasLeftSenderWindow ) |
bMouseInContent = IsDragInWindowContent ( theDrag, theWindow ); |
// If the mouse is in a window and it isn't hilited... |
if ( bMouseInContent && !bHasHilitedWindow ) |
{ |
tempRgn = NewRgn ( ); |
RectRgn ( tempRgn, &(**(theDocument->theText)).viewRect ); |
// ...draw the hilight... |
if ( ShowDragHilite ( theDrag, tempRgn, true ) == noErr ) |
// ... and remember it's now hilited |
bHasHilitedWindow = true; |
DisposeRgn ( tempRgn ); |
} |
GetDragMouse ( theDrag, &theMouse, 0L ); |
theOffset = HitTest ( theMouse, &hitDocument ); |
if ( theDocument == hitDocument ) |
{ |
// Do not allow tracking through the selection in the |
// window that sourced the drag. |
if ( theAttributes & kDragInsideSenderWindow ) |
if ( IsOffsetInSelection ( theOffset, theDocument->theText ) ) |
theOffset = -1; |
insertPosition = theOffset; |
// Reset flashing counter if the offset has moved. This makes the |
// caret blink only after the caret has stopped moving long enough. |
if ( theOffset != lastOffset ) |
{ |
caretMovedTime = theTime; |
bShowCaret = true; |
} |
lastOffset = theOffset; |
// Flash caret. |
if ( theTime - caretMovedTime > LMGetCaretTime ( ) ) |
{ |
bShowCaret = !bShowCaret; |
caretMovedTime = theTime; |
} |
if ( !bShowCaret ) |
theOffset = -1; |
// If caret offset has changed, move caret on screen. |
if (theOffset != gCaretOffset) |
{ |
if (gCaretOffset != -1) |
DrawCaret(gCaretOffset, theDocument->theText); |
if (theOffset != -1) |
DrawCaret(theOffset, theDocument->theText); |
} |
gCaretOffset = theOffset; |
} |
else |
{ |
lastOffset = theOffset; |
insertPosition = -1; |
} |
} |
break; |
case kDragTrackingLeaveWindow: |
// If the caret is on the screen, remove it. |
if (gCaretOffset != -1) |
{ |
DrawCaret(gCaretOffset, theDocument->theText); |
gCaretOffset = -1; |
} |
// Remove any window hiliting here. If the mouse |
// is not in the window and it's hilited... |
if ( bHasHilitedWindow ) |
// ...erase the hilight... |
if ( HideDragHilite ( theDrag ) == noErr ) |
// ...remember that nothing is hilited |
bHasHilitedWindow = false; |
break; |
case kDragTrackingLeaveHandler: |
break; |
default: |
theErr = paramErr; |
break; |
} |
return theErr; |
} |
Boolean IsOffsetInSelection ( short theOffset, TEHandle theTE ) |
{ |
return (theOffset >= (*theTE)->selStart && theOffset <= (*theTE)->selEnd); |
} |
// |
// DragIsNotInSourceWindow returns true if the drag in progress |
// is not in the same window it originated in. This is called by |
// the tracking and receive handlers. |
// |
static Boolean DragIsNotInSourceWindow ( DragReference theDrag ) |
{ |
DragAttributes currDragFlags; |
GetDragAttributes ( theDrag, &currDragFlags ); |
return !(currDragFlags & kDragInsideSenderWindow); |
} |
// |
// DragReceiver is called by the drag manager whenever an |
// item is dropped on one of the application's windows. |
// |
pascal OSErr MyReceiveHandler ( WindowPtr theWindow, void* handlerRefCon, |
DragReference theDrag ) |
{ |
#pragma unused(handlerRefCon) |
Boolean bHaveData = false; |
Boolean bMove = false; |
OSErr theErr = noErr; |
unsigned short numItems; |
int i; |
unsigned long theAttributes; |
ItemReference theItem; |
Size textSize, styleSize; |
DPtr theDocument; |
short theOffset; |
Point thePoint = { 0, 0 }; |
Ptr textData = nil; |
StScrpHandle styleHandle = nil; |
TEHandle tempTE = nil; |
Rect tempRect; |
RgnHandle tempRgn; |
Rect sourceRect, targetRect; |
tWindowOffscreen* theOffscreen = nil; |
if ( !(CanAcceptDragItems ( theDrag ) && IsDragInWindowContent ( theDrag, theWindow )) ) |
return dragNotAcceptedErr; |
theDocument = DPtrFromWindowPtr ( theWindow ); |
GetDragAttributes ( theDrag, &theAttributes ); |
// Get the location of the drop |
theErr = GetDragMouse ( theDrag, &thePoint, 0L ); |
if ( theErr ) goto CleanupAndBail; |
GlobalToLocal ( &thePoint ); // Assumes theWindow is current port |
// Map the drop location to the text |
theOffset = TEGetOffset ( thePoint, theDocument->theText ); |
// Don't allow a drop within the original selection |
if ( theAttributes & kDragInsideSenderWindow ) |
if ( IsOffsetInSelection ( theOffset, theDocument->theText ) ) |
return dragNotAcceptedErr; |
// Is this drag copying or a moving the text? |
bMove = IsDragMoving ( theDrag ); |
// First, gather all the text into a temp TE record, and then insert that into |
// the doucment. This approach makes it easy to handle things like text selection. |
SetRect ( &tempRect, 0, 0, 0, 0 ); |
tempTE = TEStyleNew ( &tempRect, &tempRect ); |
theErr = MemError ( ); |
if ( tempTE == nil || theErr ) goto CleanupAndBail; |
theErr = CountDragItems ( theDrag, &numItems ); |
if ( theErr ) goto CleanupAndBail; |
for ( i = 1; i <= numItems; i++ ) |
{ |
theErr = GetDragItemReferenceNumber ( theDrag, i, &theItem ); |
if ( theErr ) goto CleanupAndBail; |
theErr = GetFlavorDataSize ( theDrag, theItem, 'TEXT', &textSize ); |
if ( theErr ) goto CleanupAndBail; |
textData = NewPtr ( textSize ); |
theErr = MemError ( ); |
if ( textData == nil || theErr ) goto CleanupAndBail; |
theErr = GetFlavorData ( theDrag, theItem, 'TEXT', textData, &textSize, 0L ); |
if ( theErr ) goto CleanupAndBail; |
// Check for optional styl data for the TEXT. |
styleHandle = 0L; |
theErr = GetFlavorDataSize ( theDrag, theItem, 'styl', &styleSize ); |
if ( !theErr ) |
{ |
styleHandle = (StScrpHandle) NewHandle ( styleSize ); |
theErr = MemError ( ); |
if ( styleHandle == nil || theErr ) goto CleanupAndBail; |
HLock ( (Handle) styleHandle ); |
theErr = GetFlavorData ( theDrag, theItem, 'styl', *styleHandle, &styleSize, 0L ); |
if ( theErr ) goto CleanupAndBail; |
HUnlock ( (Handle) styleHandle ); |
} |
// Insert this drag item's text into the tempTE. |
TESetSelect ( 32767, 32767, tempTE ); |
TEStyleInsert ( textData, textSize, styleHandle, tempTE ); |
DisposePtr ( textData ); |
textData = nil; |
if ( styleHandle ) |
{ |
DisposeHandle ( (Handle) styleHandle ); |
styleHandle = nil; |
} |
} |
// Pull the TEXT and styl data out of the tempTE handle. |
textData = NewPtr (textSize = (**tempTE).teLength ); |
theErr = MemError ( ); |
if ( textData == nil || theErr ) goto CleanupAndBail; |
BlockMoveData ( *(*tempTE)->hText, textData, textSize ); |
TESetSelect ( 0, 32767, tempTE ); |
styleHandle = TEGetStyleScrapHandle (tempTE ); |
TEDispose ( tempTE ); |
tempTE = nil; |
// Insert any text into the destination. |
if ( textSize != 0 ) |
{ |
// Get rid of the hilite/caret before inserting |
if ( theAttributes & kDragHasLeftSenderWindow ) |
HideDragHilite ( theDrag ); |
if ( gCaretOffset != -1 ) |
{ |
DrawCaret ( gCaretOffset, theDocument->theText ); |
gCaretOffset = -1; |
} |
// If the drag occurred completely within the same window and the window is not |
// frontmost, bring the window forward and update its contents before completing |
// the drag. |
if ( (theAttributes & kDragInsideSenderWindow) && (theWindow != FrontWindow ( )) ) |
{ |
SelectWindow ( theWindow ); |
DoUpdate ( theWindow ); |
TEActivate ( theDocument->theText ); |
} |
// If the window is not active, must activate TE before inserting |
// text or the background hilite will not update correctly. |
if ( !IsWindowHilited ( theWindow ) ) |
TEActivate ( theDocument->theText ); |
// Draw everything into offscreen pixmap. |
theOffscreen = DrawOffscreen ( theWindow ); |
if ( theOffscreen ) |
(*theDocument->theText)->inPort = (GrafPtr) theOffscreen->offscreenWorld; |
if ( bMove ) |
{ |
// Get the current hilite rgn for zooming (source) |
tempRgn = NewRgn ( ); |
theErr = MemError ( ); |
if ( theErr ) goto CleanupAndBail; |
GetSelectedTextRgn ( theDocument, tempRgn ); |
sourceRect = (*tempRgn)->rgnBBox; |
LocalRectToGlobalRect ( &sourceRect, theWindow ); |
// If this is a move operation, delete the old text. |
DeleteTextSelection ( theDocument->theText, &theOffset ); |
} |
InsertTextAtOffset ( theOffset, textData, textSize, styleHandle, theDocument->theText ); |
// If the text is moving (not copying) within the same window, provide a ZoomRects |
// from the source to the destination before revealing the reflowed text. |
if ( bMove ) |
{ |
// Get the current hilite rgn for zooming (target) |
GetSelectedTextRgn ( theDocument, tempRgn ); |
targetRect = (*tempRgn)->rgnBBox; |
DisposeRgn ( tempRgn ); |
tempRgn = nil; |
LocalRectToGlobalRect ( &targetRect, theWindow ); |
ZoomRects ( &sourceRect, &targetRect, 12, kZoomDecelerate ); |
} |
} |
// Undo the TEActivate, if needed. |
if ( !IsWindowHilited ( theWindow ) ) |
TEDeactivate ( theDocument->theText ); |
// Show the offscreen bitmap. |
if ( theOffscreen ) |
{ |
theOffscreen = DrawOnscreen ( theOffscreen ); |
(*theDocument->theText)->inPort = theWindow; |
} |
// Make the document dirty |
theDocument->dirty = true; |
CleanupAndBail: |
// All of these will be nil if no error occurred |
if ( textData ) |
DisposePtr ( textData ); |
if ( styleHandle ) |
DisposeHandle ( (Handle) styleHandle ); |
if ( tempTE ) |
TEDispose ( tempTE ); |
if ( tempRgn ) |
DisposeRgn ( tempRgn ); |
// Normally nil since DrawOnscreen calls DisposeOffscreen |
if ( theOffscreen ) |
{ |
DisposeOffscreen ( theOffscreen ); |
(*theDocument->theText)->inPort = theWindow; |
} |
return theErr; |
} |
void DeleteTextSelection ( TEHandle theTE, short* theInsertPosition ) |
{ |
short selStart, selEnd; |
selStart = (*theTE)->selStart; |
selEnd = (*theTE)->selEnd; |
if ( WhiteSpaceAtOffset ( selStart - 1, theTE ) && |
!WhiteSpaceAtOffset ( selStart, theTE ) && |
!WhiteSpaceAtOffset ( selEnd - 1, theTE ) && |
WhiteSpaceAtOffset ( selEnd, theTE ) ) |
{ |
if ( GetCharAtOffset ( selEnd, theTE ) == ' ' ) |
(*theTE)->selEnd++; |
} |
if ( *theInsertPosition > selStart ) |
*theInsertPosition -= ((*theTE)->selEnd - (*theTE)->selStart); |
TEDelete ( theTE ); |
return; |
} |
static Boolean IsDragMoving ( DragReference theDrag ) |
{ |
DragAttributes theAttributes; |
GetDragAttributes ( theDrag, &theAttributes ); |
return (theAttributes & kDragInsideSenderWindow) && !IsDragWithOptionKey ( theDrag ); |
} |
static Boolean IsDragWithOptionKey ( DragReference theDrag ) |
{ |
short mouseDownModifiers, mouseUpModifiers; |
GetDragModifiers ( theDrag, 0L, &mouseDownModifiers, &mouseUpModifiers ); |
return (mouseDownModifiers & optionKey) | (mouseUpModifiers & optionKey); |
} |
void OutlineRegion ( RgnHandle theRgn ) |
{ |
RgnHandle tempRgn; |
tempRgn = NewRgn ( ); |
CopyRgn ( theRgn, tempRgn ); |
InsetRgn ( tempRgn, 1, 1 ); |
DiffRgn ( theRgn, tempRgn, theRgn ); |
DisposeRgn ( tempRgn ); |
return; |
} |
OSErr DoWindowContentDrag ( WindowPtr theWindow, EventRecord* theEvent ) |
{ |
OSErr theErr = noErr; |
DragReference theDrag = (unsigned long) nil; |
RgnHandle dragRgn = nil; |
Ptr dataPtr = nil; |
StScrpHandle styleHandle = nil; |
short dataSize; |
DPtr theDocument; |
Rect dragBounds; |
ItemReference theItem; |
// create a new drag |
theErr = NewDrag ( &theDrag ); |
if ( theErr ) goto CleanupAndBail; |
// use the window ptr as item reference for the heck of it |
theItem = (ItemReference) theWindow; |
// add the data to the drag |
theDocument = DPtrFromWindowPtr ( theWindow ); |
dataPtr = NewPtr ( 0L ); |
theErr = GetSelectedText ( theDocument, dataPtr, &dataSize ); |
if ( theErr || dataSize == 0 ) goto CleanupAndBail; |
theErr = AddDragItemFlavor ( theDrag, theItem, 'TEXT', dataPtr, dataSize, 0 ); |
if ( theErr ) goto CleanupAndBail; |
DisposePtr ( dataPtr ); |
dataPtr = nil; |
// Add style data |
styleHandle = TEGetStyleScrapHandle ( theDocument->theText ); |
HLock ( (Handle) styleHandle ); |
AddDragItemFlavor ( theDrag, theItem, 'styl', (Ptr) *styleHandle, GetHandleSize ( (Handle) styleHandle ), 0 ); |
HUnlock ( (Handle) styleHandle ); |
DisposeHandle ( (Handle) styleHandle ); |
styleHandle = nil; |
// generate the bounds and region for the drag using the window's |
// content rectangle |
dragBounds = (**((WindowPeek) theWindow)->contRgn).rgnBBox; |
theErr = SetDragItemBounds(theDrag, theItem, &dragBounds); |
if ( theErr ) goto CleanupAndBail; |
dragRgn = NewRgn ( ); |
GetSelectedTextRgn ( theDocument, dragRgn ); |
LocalRgnToGlobalRgn ( dragRgn, nil ); |
OutlineRegion ( dragRgn ); |
// do the drag and clean up |
TrackDrag ( theDrag, theEvent, dragRgn ); |
if ( DragIsNotInSourceWindow ( theDrag ) ) |
{ |
AEDesc dropLocation; |
// Get the drop location |
GetDropLocation ( theDrag, &dropLocation ); |
if ( !IsDragWithOptionKey ( theDrag ) && DropLocationIsFinderTrash ( &dropLocation) ) |
{ |
// Delete the exact text. Don't call DeleteTextSelection |
TEDelete ( theDocument->theText ); |
theDocument->dirty = true; |
} |
AEDisposeDesc ( &dropLocation ); |
} |
CleanupAndBail: |
if ( theDrag ) |
DisposeDrag(theDrag); |
if ( dragRgn ) |
DisposeRgn ( dragRgn ); |
// These should be nil |
if ( dataPtr ) |
DisposePtr ( dataPtr ); |
if ( styleHandle ) |
DisposeHandle ( (Handle) styleHandle ); |
return theErr; |
} |
void LocalRgnToGlobalRgn ( RgnHandle theRgn, WindowRef theWindow ) |
{ |
GrafPtr savePort; |
Point localPt, globalPt; |
if ( theWindow ) |
{ |
GetPort ( &savePort ); |
SetPortWindowPort ( theWindow ); |
} |
localPt = globalPt = *(Point*) &(*theRgn)->rgnBBox; // top left |
LocalToGlobal ( &globalPt ); |
SubPt ( localPt, &globalPt ); |
OffsetRgn ( theRgn, globalPt.h, globalPt.v ); |
if ( theWindow ) |
SetPort ( savePort ); |
return; |
} |
void LocalRectToGlobalRect ( Rect* theRect, WindowRef theWindow ) |
{ |
GrafPtr savePort; |
Point localPt, globalPt; |
if ( theWindow ) |
{ |
GetPort ( &savePort ); |
SetPortWindowPort ( theWindow ); |
} |
localPt = globalPt = *(Point*) theRect; // top left |
LocalToGlobal ( &globalPt ); |
SubPt ( localPt, &globalPt ); |
OffsetRect ( theRect, globalPt.h, globalPt.v ); |
if ( theWindow ) |
SetPort ( savePort ); |
return; |
} |
OSErr GetSelectedText ( DPtr theDocument, Ptr dataPtr, short* dataSize ) |
{ |
OSErr theErr; |
TEHandle teHandle = theDocument->theText; |
*dataSize = (**(teHandle)).selEnd - (**(teHandle)).selStart; |
if ( *dataSize ) |
{ |
SetPtrSize ( dataPtr, *dataSize ); |
theErr = MemError ( ); |
if ( theErr ) |
return theErr; |
BlockMoveData ( *(**(teHandle)).hText + (**(teHandle)).selStart, dataPtr, *dataSize ); |
} |
return noErr; |
} |
void GetSelectedTextRgn ( DPtr theDocument, RgnHandle dataRgn ) |
{ |
TEGetHiliteRgn ( dataRgn, theDocument->theText ); |
return; |
} |
// |
// Does the user want to drag something? First, checks the click could |
// be a drag, then waits to see if the user starts to drag the text. |
// |
Boolean UserWantsToDrag ( WindowRef theWindow, Point globalPt ) |
{ |
Point localPt; |
localPt = globalPt; |
GlobalToLocal ( &localPt ); // Assumes theWindow is current port |
if ( PointInWindowSelection ( localPt, theWindow ) ) |
return WaitMouseMoved ( globalPt ); |
return false; |
} |
// |
// Returns true if the local point is in the window's current selection |
// |
Boolean PointInWindowSelection ( Point localPt, WindowRef theWindow ) |
{ |
Boolean bHit; |
RgnHandle tempRgn; |
DPtr theDocument; |
theDocument = DPtrFromWindowPtr ( theWindow ); |
if ( !(PtInDocument ( localPt, theDocument )) ) |
return false; |
tempRgn = NewRgn ( ); |
GetSelectedTextRgn ( theDocument, tempRgn ); |
bHit = PtInRgn ( localPt, tempRgn ); |
DisposeRgn ( tempRgn ); |
return bHit; |
} |
// |
// Given a point in global coordinates, HitTest returns a pointer to a |
// document structure if the point is inside a document window on the screen. |
// If the point is not inside a document window, HitTest return NULL in |
// theDoc. If the point is in a doument window and also in the viewRect of |
// the document's TextEdit field, HitTest also returns the offset into |
// the text that corresponds to that point. If the point is not in the text, |
// HitTest returns -1. |
// |
short HitTest(Point theLoc, DPtr* theDoc) |
{ |
GrafPtr savePort; |
WindowPtr theWindow; |
short offset; |
*theDoc = 0L; |
offset = -1; |
if (FindWindow(theLoc, &theWindow) == inContent) |
{ |
if ( Ours ( theWindow ) ) |
{ |
*theDoc = DPtrFromWindowPtr ( theWindow ); |
GetPort ( &savePort ); |
SetPort(theWindow); |
GlobalToLocal(&theLoc); |
SetPort ( savePort ); |
if ((PtInRect(theLoc, &(**((**theDoc).theText)).viewRect)) && |
(PtInRect(theLoc, &(**((**theDoc).theText)).destRect))) { |
offset = TEGetOffset(theLoc, (**theDoc).theText); |
if ((TEIsFrontOfLine(offset, (**theDoc).theText)) && (offset) && |
((*((**((**theDoc).theText)).hText))[offset - 1] != 0x0D) && |
(TEGetPoint(offset - 1, (**theDoc).theText).h < theLoc.h)) { |
offset--; |
} |
} |
} |
} |
return(offset); |
} |
// TEIsFrontOfLine, given an offset and a TextEdit handle, returns true if |
// the given offset is at the beginning of a line start. |
short TEIsFrontOfLine ( short offset, TEHandle theTE ) |
{ short line = 0; |
if ((**theTE).teLength == 0) |
return(true); |
if (offset >= (**theTE).teLength) |
return( (*((**theTE).hText))[(**theTE).teLength - 1] == 0x0d ); |
while ((**theTE).lineStarts[line] < offset) |
line++; |
return( (**theTE).lineStarts[line] == offset ); |
} |
// TEGetLine, given an offset and a TextEdit handle, returns the line number |
// of the line that contains the offset. |
short TEGetLine ( short offset, TEHandle theTE ) |
{ short line = 0; |
if (offset > (**theTE).teLength) |
return((**theTE).nLines); |
while ((**theTE).lineStarts[line] < offset) |
line++; |
return(line); |
} |
// |
// DrawCaret draws a caret in a TextEdit field at the given offset. DrawCaret |
// expects the port to be set to the port that the TextEdit field is in. |
// DrawCaret inverts the image of the caret onto the screen. |
// |
void DrawCaret(short offset, TEHandle theTE) |
{ Point theLoc; |
short theLine, lineHeight; |
// Get the coordinates and the line of the offset to draw the caret. |
theLoc = TEGetPoint(offset, theTE); |
theLine = TEGetLine(offset, theTE); |
// For some reason, TextEdit dosen't return the proper coordinates |
// of the last offset in the field if the last character in the record |
// is a carriage return. TEGetPoint returns a point that is one line |
// higher than expected. The following code fixes this problem. |
if ((offset == (**theTE).teLength) && |
(*((**theTE).hText))[(**theTE).teLength - 1] == 0x0D) { |
theLoc.v += TEGetHeight(theLine, theLine, theTE); |
} |
// Always invert the caret when drawing. |
PenMode(patXor); |
//Get the height of the line that the offset points to. |
lineHeight = TEGetHeight(theLine, theLine, theTE); |
// Draw the appropriate caret image. |
MoveTo(theLoc.h - 1, theLoc.v - 1); |
Line(0, 1 - lineHeight); |
PenNormal(); |
} |
char GetCharAtOffset(short offset, TEHandle theTE) |
{ |
if (offset < 0) |
return(0x0D); |
return(((char *) *((**theTE).hText))[offset]); |
} |
Boolean WhiteSpace(char theChar) |
{ |
return((theChar == ' ') || (theChar == 0x0D)); |
} |
Boolean WhiteSpaceAtOffset(short offset, TEHandle theTE) |
{ char theChar; |
if ((offset < 0) || (offset > (**theTE).teLength - 1)) |
return(true); |
theChar = ((char *) *((**theTE).hText))[offset]; |
return((theChar == ' ') || (theChar == 0x0D)); |
} |
void InsertTextAtOffset(short offset, char *theBuf, long size, StScrpHandle theStyl, TEHandle theTE) |
{ |
if (size == 0) |
return; |
// If inserting at the end of a word and the selection does not begin with |
// a space, insert a space before the insertion. |
if (!WhiteSpaceAtOffset(offset - 1, theTE) && |
WhiteSpaceAtOffset(offset, theTE) && |
!WhiteSpace(theBuf[0])) { |
TESetSelect(offset, offset, theTE); |
TEKey(' ', theTE); |
offset++; |
} |
// If inserting at the beginning of a word and the selection does not end |
// with a space, insert a space after the insertion. |
if ( WhiteSpaceAtOffset(offset - 1, theTE) && |
!WhiteSpaceAtOffset(offset, theTE) && |
!WhiteSpace(theBuf[size - 1])) { |
TESetSelect(offset, offset, theTE); |
TEKey(' ', theTE); |
} |
TESetSelect(offset, offset, theTE); |
TEStyleInsert(theBuf, size, theStyl, theTE); |
TESetSelect(offset, offset + size, theTE); |
return; |
} |
// |
// DropLocationIsFinderTrash returns true if the given dropLocation |
// AEDesc is a descriptor of the Finder's Trash. |
// |
Boolean DropLocationIsFinderTrash ( AEDesc* dropLocation ) |
{ OSErr result; |
AEDesc dropSpec; |
FSSpecPtr theSpec; |
CInfoPBRec thePB; |
short trashVRefNum; |
long trashDirID; |
// Coerce the dropLocation descriptor to an FSSpec. If there's no dropLocation or |
// it can't be coerced into an FSSpec, then it couldn't have been the Trash. |
if ( (dropLocation->descriptorType != typeNull) && |
(AECoerceDesc(dropLocation, typeFSS, &dropSpec) == noErr)) |
{ |
HLock(dropSpec.dataHandle); |
theSpec = (FSSpec *) *dropSpec.dataHandle; |
// Get the directory ID of the given dropLocation object. |
thePB.dirInfo.ioCompletion = 0L; |
thePB.dirInfo.ioNamePtr = (StringPtr) &theSpec->name; |
thePB.dirInfo.ioVRefNum = theSpec->vRefNum; |
thePB.dirInfo.ioFDirIndex = 0; |
thePB.dirInfo.ioDrDirID = theSpec->parID; |
result = PBGetCatInfo(&thePB, false); |
HUnlock(dropSpec.dataHandle); |
AEDisposeDesc(&dropSpec); |
if ( result ) |
return(false); |
// If the result is not a directory, it cannot be the Trash. |
if (!(thePB.dirInfo.ioFlAttrib & (1 << 4))) |
return(false); |
// Get information about the Trash folder. |
FindFolder ( theSpec->vRefNum, kTrashFolderType, kCreateFolder, &trashVRefNum, &trashDirID); |
// If the directory ID of the dropLocation object is the same as the directory ID |
// returned by FindFolder, then the drop must have occurred into the Trash. |
if ( thePB.dirInfo.ioDrDirID == trashDirID ) |
return true; |
} |
return false; |
} |
Copyright © 2003 Apple Computer, Inc. All Rights Reserved. Terms of Use | Privacy Policy | Updated: 2003-07-22