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.
TextSections.c
/*------------------------------------------------------------------------------ |
* |
* Apple Developer Technical Support |
* |
* Text section handling routines |
* |
* Program: AEObject-Edition Sample |
* File: TextSections.c - C Source |
* |
* by: C.K. Haun <TR> |
* |
* Copyright © 1990-1992 Apple Computer, Inc. |
* All rights reserved. |
* |
*------------------------------------------------------------------------------ |
* This file contains the functions for manipulating and bookkeeping |
* for published and subscribed text sections in this sample. |
*----------------------------------------------------------------------------*/ |
#define __TEXTSECTIONS__ |
#pragma segment TextHandler |
#include "Sampdefines.h" |
static Boolean Touching(mySectionDataHandle checkSection, TEHandle teText); |
extern void MyHiHook(void); |
Rect nulRect={0,0,0,0}; |
/* the following few functions are for text, and will be expanded as this is fully implemented */ |
Boolean HasTESelection(windowCHandle inWind) |
{ |
TEHandle temp; |
if (inWind == nil) |
return(false); |
temp = (*inWind)->boxHandle; |
if (temp != nil) |
return((*temp)->selStart != (*temp)->selEnd); |
else |
return(false); |
} |
Boolean PasteText(void) |
{ |
windowCHandle tempWC; |
tempWC = (windowCHandle)GetWRefCon(FrontWindow() ); |
if ((*tempWC)->boxHandle != nil && gClipHasContents == kClipHasText) { |
TEFromScrap(); |
CheckTextSections(tempWC, kAdding); |
TEPaste((*tempWC)->boxHandle); |
return(true); |
} |
return(false); |
} |
Boolean CutText(void) |
{ |
windowCHandle tempWC; |
tempWC = (windowCHandle)GetWRefCon(FrontWindow() ); |
if (HasTESelection(tempWC)) { |
CheckTextSections(tempWC, kRemoving); /* adjusts the end points on text sections */ |
TECut((*tempWC)->boxHandle); |
ZeroScrap(); |
TEToScrap(); |
return(true); |
} |
return(false); |
} |
Boolean CopyText(void) |
{ |
windowCHandle tempWC; |
tempWC = (windowCHandle)GetWRefCon(FrontWindow() ); |
if (HasTESelection(tempWC)) { |
TECopy((*tempWC)->boxHandle); |
ZeroScrap(); |
TEToScrap(); |
return(true); |
} |
return(false); |
} |
Boolean ClearText(void) |
{ |
windowCHandle tempWC; |
tempWC = (windowCHandle)GetWRefCon(FrontWindow() ); |
if (HasTESelection(tempWC)) { |
CheckTextSections(tempWC, kRemoving); |
TEDelete((*tempWC)->boxHandle); |
return(true); |
} |
return(false); |
} |
/* This checks all the publishers and subscribers in the current TE record, and sees */ |
/* if their data needs to be modified based on the TE action pending */ |
void CheckTextSections(windowCHandle inWindow, short Action) |
{ |
TEHandle theTEHandle = (*inWindow)->boxHandle; |
mySectionDataHandle startSection = (*inWindow)->textSections; |
Boolean testFlag = true; /* a gen purp flag please */ |
Boolean whapBorder = false; |
mySectionDataHandle currentSection = startSection; |
short counter = 0; |
/* see if the insertion point or selection range is after all our sections. */ |
/* if it is (a forlorn hope) we can exit without adjusting anything. */ |
if (startSection == nil) |
return; /* no sections, go away */ |
do { |
if ((*currentSection)->endChar > (*theTEHandle)->selStart) { |
testFlag = false; |
counter++; /* a minimal aid to exiting this stuff later */ |
if((*currentSection)->bordered)whapBorder=true; |
} |
currentSection = (*currentSection)->nextSection; |
} while (currentSection); |
if (testFlag) |
return; /* no section is affected by this action, go away */ |
/* OK, someone (or two, or twenty) will be affected by this action */ |
/* so we have to see who, and how much, and if it's like adding in the middle, */ |
/* or like shifting start and end points down, or like whatever, y'know? */ |
currentSection = (*inWindow)->textSections; |
switch (Action) { |
unsigned long actionLen; |
case kKeyStroke: |
/* so we bump the start and end positions of all the sections after this point by 1 */ |
while (counter) { /* minimal aid in exiting this thing */ |
if ((*theTEHandle)->selStart <= (*currentSection)->startChar) { |
(*currentSection)->startChar++; |
(*currentSection)->endChar++; |
counter--; |
} else { |
if ((*theTEHandle)->selStart > (*currentSection)->startChar && |
(*theTEHandle)->selStart < (*currentSection)->endChar) { |
/* this should mean that the input char is between the start and the end */ |
/* so the publisher expands by 1 */ |
(*currentSection)->endChar++; |
counter--; |
} |
} |
currentSection = (*currentSection)->nextSection; |
if (currentSection == nil) |
break; /* hey, y'know? */ |
} |
break; |
case kAdding: |
/* increase start and finished positions by the size of the clip being added */ |
actionLen = TEGetScrapLength(); |
/* so we bump the start and end positions of all the sections after this point by 1 */ |
while (counter) { /* minimal aid in exiting this thing */ |
if ((*currentSection)->startChar > (*theTEHandle)->selStart) { |
(*currentSection)->startChar += actionLen; |
(*currentSection)->endChar += actionLen; |
counter--; |
} else {Boolean temp=false; |
/* expand the published section */ |
temp=(*currentSection)->bordered; |
(*currentSection)->endChar += actionLen; |
} |
currentSection = (*currentSection)->nextSection; |
if (currentSection == nil) |
break; /* hey, y'know? */ |
} |
break; |
case kRemoving: |
/* decrease start and finished positions by the size of the current selection */ |
/* You have another worry in this area, of course. If the user has highlighted */ |
/* the WHOLE publisher or subscriber, that means that you will be deleting */ |
/* it completely */ |
actionLen = (*currentSection)->endChar - (*currentSection)->startChar; |
/* so we bump the start and end positions of all the sections after this point by 1 */ |
while (counter) { /* minimal aid in exiting this thing */ |
if ((*currentSection)->startChar > (*theTEHandle)->selStart) { |
(*currentSection)->startChar -= actionLen; |
(*currentSection)->endChar -= actionLen; |
counter--; |
} |
currentSection = (*currentSection)->nextSection; |
if (currentSection == nil) |
break; |
} |
break; |
} |
/* if anything changed and borders are on in any section, we need to */ |
/* erase and redraw the borders */ |
/* This will cause a lot of screen flicker, you can do it neater in your app */ |
if(whapBorder)InvalRect(&(*theTEHandle)->viewRect); |
} |
/* KillTextBorders does about what you'd expect, it turns off all the */ |
/* text borders */ |
void KillTextBorders(void) |
{windowCHandle tempWC = (windowCHandle)GetWRefCon(FrontWindow() ); |
mySectionDataHandle startSection = (*tempWC)->textSections; |
while(startSection){ |
(*startSection)->bordered = false; |
startSection = (*startSection)->nextSection; |
} |
} |
void ShowAllTextBorders(void) |
{windowCHandle tempWC = (windowCHandle)GetWRefCon(FrontWindow() ); |
mySectionDataHandle startSection = (*tempWC)->textSections; |
while(startSection){ |
(*startSection)->bordered = true; |
startSection = (*startSection)->nextSection; |
} |
} |
/* SkipOverSubscriber is used to move the insertion point around any subscriptions */ |
/* in response to a cursor or delete key action */ |
/* This is, of course, because you cannot edit the contents of a subscription */ |
Boolean SkipOverSubscriber(windowCHandle inWindow, unsigned short theKey) |
{ |
long oldStart, oldEnd; |
Boolean flagBack = false; |
short newInsertionPoint; /* where the insertion point will be moving to as */ |
/* a result of this key */ |
/* first, as usually, see if this action impinges on the target */ |
/* of course, this only counts for subscriptions */ |
TEHandle theTEHandle = (*inWindow)->boxHandle; |
mySectionDataHandle startSection = (*inWindow)->textSections; |
if (!startSection) |
return(flagBack); /* scat if no sections */ |
do { |
/* first see if it's a publisher, if it is, skip it. */ |
if (!(*startSection)->publishing) { |
switch (theKey) { /* actions based on what direction this key is moving us */ |
case kLeftArrow: |
newInsertionPoint = ((*theTEHandle)->selStart) - 1; |
if (newInsertionPoint <= (*startSection)->endChar && newInsertionPoint >= (*startSection)->startChar) { |
flagBack = true; |
TESetSelect(((*startSection)->startChar), ((*startSection)->startChar), theTEHandle); |
} |
break; |
case kRightArrow: |
newInsertionPoint = ((*theTEHandle)->selEnd) + 1; |
if (newInsertionPoint >= (*startSection)->startChar && newInsertionPoint <= (*startSection)->endChar) { |
flagBack = true; |
TESetSelect(((*startSection)->endChar), ((*startSection)->endChar), theTEHandle); |
} |
break; |
case kUpArrow: |
case kDownArrow: |
/* for up and down arrows, we actually have to do the TEKey thing here, then evaluate the */ |
/* result and see if it landed in a subscription. This is of course because we're using */ |
/* TextEdit, if you're doing your own text processing you will know where any keystroke lands */ |
/* Of course, you gotta save the current position of the insertion point first */ |
oldStart = (*theTEHandle)->selStart; |
oldEnd = (*theTEHandle)->selEnd; |
TEKey(theKey, theTEHandle); |
/* see where it ended up */ |
newInsertionPoint = (*theTEHandle)->selEnd; |
if (newInsertionPoint >= (*startSection)->startChar && newInsertionPoint <= (*startSection)->endChar) { |
flagBack = true; |
/* now move before or after the section depending on the arrow */ |
if (theKey == kUpArrow) |
TESetSelect(((*startSection)->startChar), ((*startSection)->startChar), theTEHandle); |
else |
TESetSelect(((*startSection)->endChar), ((*startSection)->endChar), theTEHandle); |
} else { |
TESetSelect(oldStart, oldEnd, theTEHandle); |
} |
break; |
} |
} |
startSection = (*startSection)->nextSection; /* go to next in list */ |
} while (startSection); |
return(flagBack); |
} |
/* RePackText makes sure we have the proper text in our lil publisher before writing the */ |
/* thing out. */ |
/* Actually, since this function exists, we don't really need to keep a copy of the text attached */ |
/* to the publisher. */ |
void RePackText(mySectionDataHandle currentSection, TEHandle theTEHandle) |
{ |
short theLength; |
CharsHandle theRawText; |
Ptr src; |
theLength = (*currentSection)->endChar - (*currentSection)->startChar; |
SetHandleSize((*currentSection)->additionalData, theLength); |
HLock((*currentSection)->additionalData); |
theRawText = TEGetText(theTEHandle); |
HLock((Handle)theRawText); |
src = (Ptr)*theRawText; |
src = src + (*currentSection)->startChar; |
BlockMove((Ptr)((Ptr)*theRawText)+(*currentSection)->startChar, (Ptr)*((*currentSection)->additionalData), theLength); |
HUnlock((Handle)theRawText); |
HUnlock((*currentSection)->additionalData); |
} |
/* InTextBox tells us if the rectangle passed intersects the text rectangle */ |
/* in the current window. We use this for; */ |
/* 1) Seeing if the mouse click was in the TERect, so we can call TEClick */ |
/* 2) See if one of our drawing commands will overdraw the text, which is BaD */ |
Boolean InTextBox(windowCHandle inName, Rect *where) |
{ |
Rect temp; |
if ((*inName)->boxHandle == nil) { |
return(false); |
} else { |
return(SectRect(where, &(*inName)->textBox, &temp)); |
} |
} |
/* CheckSubscriberHit sees if the user person clicked inside a subscription. If they did, then */ |
/* we'll either highlight the whole subscriber and <sometime> draw a border around it, or */ |
/* extend the selection range to include the whole subscription if they dragged into just part of it */ |
/* for bording, things get a little more complicated. If this is a publisher, and it's already */ |
/* showing a border, we want to do a normal TEClick since the user can edit inside a publisher */ |
void CheckSectionHit() |
{ |
windowCHandle shortName = (windowCHandle)GetWRefCon(FrontWindow()); |
TEHandle theTEHandle = (*shortName)->boxHandle; |
long mySelStart = (*theTEHandle)->selStart; /* added these variables just to make the code a */ |
long mySelEnd = (*theTEHandle)->selEnd; /* little easier to read */ |
long newStart = mySelStart; /* if any adjustments need to be made */ |
long newEnd = mySelEnd; |
Boolean startedBordered; |
mySectionDataHandle startSection = (*shortName)->textSections; |
mySectionDataHandle currentSection = startSection; |
/* and of course, the affected window is the front window, since they clicked there, y'know */ |
/* what we do here is see if the current mouse click lands in a publisher or */ |
/* subscriber. If it's a publisher, check for a double click and highlite. */ |
/* if the selection range covers ANY part of a subscriber, extend the selection to */ |
/* encompass the whole subscriber. Then border the subscriber also. */ |
/* This means that we cannot exit after finding one pub or sub, we must */ |
/* check all since the user could have selected all the text in the record */ |
while (currentSection) { |
startedBordered = (*currentSection)->bordered; |
if (Touching(currentSection, theTEHandle)) { |
/* see if this is a subscriber. If so, extend start and end positions to include it all */ |
if (!(*currentSection)->publishing) { |
/* it is a subscriber */ |
if ((*currentSection)->startChar < mySelStart) |
newStart = (*currentSection)->startChar; |
if ((*currentSection)->endChar > mySelEnd) |
newEnd = (*currentSection)->endChar; |
TESetSelect(newStart, newEnd, theTEHandle); |
} else { |
/* for a publisher, highlith the whole thing if it's the first click. If it's the */ |
/* second (or subsequent, o' course) they want to edit, so let them */ |
if (startedBordered) |
TESetSelect(newStart, newEnd, theTEHandle); |
else |
TESetSelect((*currentSection)->startChar, (*currentSection)->endChar, theTEHandle); |
} |
} else { |
/* if this section was not hit, deborder it */ |
if(!gShowingAll && (*currentSection)->bordered){(*currentSection)->bordered=false; |
InvalRect(&(*theTEHandle)->viewRect); |
}} |
currentSection = (*currentSection)->nextSection; |
} |
} |
/* Touching was created just so I wouldn't have to have a huge if statement in */ |
/* CheckSectionHit */ |
/* All it does is see if the current selection range of the TEHandle touches or */ |
/* fully encloses any text sections */ |
static Boolean Touching(mySectionDataHandle checkSection, TEHandle teText) |
{ |
Boolean ouch = false; /* will turn true if a touch actually happened */ |
long tStart = (*teText)->selStart; |
long tEnd = (*teText)->selEnd; |
short ourStart = (*checkSection)->startChar; |
short ourEnd = (*checkSection)->endChar; |
/* first see if the start hits inside the section */ |
if (tStart > ourStart && tStart < ourEnd) |
ouch = true; |
else if (tStart < ourStart && tEnd > ourStart) |
ouch = true; /* now see if we got completely enclosed */ |
else if (tEnd > ourStart && tEnd < ourEnd) |
ouch = true; |
/* and finally see if we got touched by the end of the thing */ |
if (ouch){ |
BorderTextSection(checkSection); |
/* put this section in our gloabl section handle holder so we can do options on it */ |
gShowingSecHandle=(*checkSection)->theSection; |
if((*checkSection)->publishing) { |
gShowPub = true; |
gShowPubRect=nulRect; |
} else { |
gShowSub = true; |
gShowSubRect=nulRect; |
}} |
return(ouch); |
} |
/* ExcludeSubscriber removes subscriptions from the range of selected text, so when a keystroke */ |
/* replaces a selection, the subscriber does not also get replaced. */ |
void ExcludeSubscriber(windowCHandle tempCH) |
{ |
#pragma unused (tempCH) |
} |
/* this borders the selected section. It's a pain. */ |
/* BUT, it's a pain because I'm using TextEdit. If you're writing a word processor */ |
/* or another application which does it's own text processing, this will be a lot easier */ |
/* ¥¥¥¥ NOTE: this doesn't look very good when a subscriber is embedded in a publisher, since */ |
/* the lines of course overlap. I don't know how to deal with this yet. */ |
void BorderTextSection(mySectionDataHandle theText) |
{ |
TEHandle theTEHandle; |
WindowPtr theWindow; |
windowCHandle shortName; |
PolyHandle borderPoly; |
long origIStart; |
long origIEnd; |
long newStart; |
long newEnd; |
Point startPoint; |
Point endPoint; |
RgnHandle oldClip = NewRgn(); |
WindowPtr oldPort; |
GetPort(&oldPort); |
GetClip(oldClip); |
theWindow = FindSection((*theText)->theSection); |
SetPort(theWindow); |
shortName = (windowCHandle)GetWRefCon(theWindow); |
HLock((Handle)shortName); |
theTEHandle = (*shortName)->boxHandle; |
origIStart = (*theTEHandle)->selStart; |
origIEnd = (*theTEHandle)->selEnd; |
TESetSelect((*theText)->startChar, (*theText)->endChar, theTEHandle); |
newStart = (*theTEHandle)->selStart; |
newEnd = (*theTEHandle)->selEnd; |
ClipRect(&(*theTEHandle)->viewRect); |
/* so our poly doesn't exceed the bounds of our rect */ |
/* now what we do is create a Polygon that's the shape of the text section */ |
startPoint = TEGetPoint((short)newStart, theTEHandle); |
endPoint = TEGetPoint((short)newEnd, theTEHandle); |
/* ¥¥¥¥ NOTE: This is a really simplistic check, since this is a simple sample. */ |
/* In a more complex text document you'd be worrying about text scrolled out of the view rect, */ |
/* weird regions, and other things I'm not going to deal with here. */ |
/* see if the verts are the same. If they are, then all we need is a simple rect */ |
if (startPoint.v == endPoint.v) { |
borderPoly = OpenPoly(); |
startPoint.v -= (*theTEHandle)->lineHeight; /* top o' the line to you! */ |
MoveTo(startPoint.h, startPoint.v); |
LineTo(endPoint.h, startPoint.v); |
LineTo(endPoint.h, endPoint.v); |
LineTo(startPoint.h, endPoint.v); |
LineTo(startPoint.h, startPoint.v); |
ClosePoly(); |
PenSize(3, 3); |
if ((*theText)->publishing) |
PenPat(&qd.gray); |
else |
PenPat(&qd.dkGray); |
FramePoly(borderPoly); |
PenPat(&qd.black); |
PenSize(1, 1); |
} else { |
short endTop = endPoint.v -(*theTEHandle)->lineHeight; |
/* need to build a slightly more complex poly */ |
borderPoly = OpenPoly(); |
startPoint.v -= (*theTEHandle)->lineHeight; /* top o' the line to you! */ |
MoveTo(startPoint.h, startPoint.v); |
LineTo((*theTEHandle)->destRect.right, startPoint.v); |
LineTo((*theTEHandle)->destRect.right, endTop); |
LineTo(endPoint.h, endTop); /* may not be needed, but doesn't hurt anything */ |
LineTo(endPoint.h, endPoint.v); |
LineTo((*theTEHandle)->destRect.left, endPoint.v); |
LineTo((*theTEHandle)->destRect.left, (startPoint.v + ((*theTEHandle)->lineHeight))); |
LineTo(startPoint.h, (startPoint.v + ((*theTEHandle)->lineHeight))); |
LineTo(startPoint.h, startPoint.v); |
ClosePoly(); |
PenSize(3, 3); |
if ((*theText)->publishing) |
PenPat(&qd.gray); |
else |
PenPat(&qd.dkGray); |
FramePoly(borderPoly); |
PenPat(&qd.black); |
PenSize(1, 1); |
} |
if((*theText)->bordered) |
/* if it started bordered, and it's a publisher, then when set the selection range to */ |
/* what it was when we entered */ |
TESetSelect(origIStart,origIEnd, theTEHandle); |
else |
(*theText)->bordered = true; /* log it bordered */ |
KillPoly(borderPoly); |
SetClip(oldClip); |
DisposeRgn(oldClip); |
SetPort(oldPort); |
HUnlock((Handle)shortName); |
} |
#undef __TEXTSECTIONS__ |
Copyright © 2003 Apple Computer, Inc. All Rights Reserved. Terms of Use | Privacy Policy | Updated: 2003-01-14