1 line
28 KiB
C
Executable File
1 line
28 KiB
C
Executable File
/* Copyright (c) 2017, Computer History Museum
|
||
All rights reserved.
|
||
Redistribution and use in source and binary forms, with or without modification, are permitted (subject to
|
||
the limitations in the disclaimer below) provided that the following conditions are met:
|
||
* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
|
||
* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following
|
||
disclaimer in the documentation and/or other materials provided with the distribution.
|
||
* Neither the name of Computer History Museum nor the names of its contributors may be used to endorse or promote products
|
||
derived from this software without specific prior written permission.
|
||
NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE
|
||
COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||
HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
|
||
DAMAGE. */
|
||
|
||
#ifndef SPELL_H
|
||
#include "spell.h"
|
||
#include "ssce.h"
|
||
#endif
|
||
|
||
#pragma segment Spell
|
||
|
||
#ifdef WINTERTREE
|
||
#define FILE_NUM 74
|
||
|
||
#define SPELL_OPT_MASK 0xf
|
||
#define SPELL_SUGGEST_BOTH (1L<<20)
|
||
|
||
Boolean SpellWordKnown(PStr word,PStr replace);
|
||
short SpellOpenLexDir(FSSpecPtr inSpec);
|
||
OSErr SpellOpenLex(FSSpecPtr spec);
|
||
void SpellGetCurrentWord(PStr word,Boolean *checked,Boolean *writeable, Boolean *misspelled,long *start,long *stop);
|
||
void SpellSuggestions(PStr word,UHandle *suggestions);
|
||
OSErr SpellReplace(PETEHandle pte,PStr replaceMe);
|
||
OSErr SpellRemove(PETEHandle pte);
|
||
OSErr SpellAdd(PETEHandle pte);
|
||
OSErr SpellNext(PETEHandle pte);
|
||
OSErr SpellMakeUserDict(FSSpecPtr dirSpec,short id);
|
||
static SpellInUse;
|
||
static SpellUserDict = -1;
|
||
static SpellUserADict = -1;
|
||
static SpellTicks;
|
||
|
||
/************************************************************************
|
||
* SpellOpen - open a spelling session
|
||
************************************************************************/
|
||
short SpellOpen(void)
|
||
{
|
||
FSSpec mySpec;
|
||
short howMany=0;
|
||
long domains[]={kNetworkDomain,kSystemDomain,kLocalDomain,kUserDomain};
|
||
FSSpec oldSpec;
|
||
short n = sizeof(domains)/sizeof(domains[0]);
|
||
|
||
Zero(oldSpec);
|
||
|
||
if (!HaveSpeller()) return(-1);
|
||
|
||
SpellInUse++;
|
||
|
||
if (SpellSession<0)
|
||
{
|
||
SpellSession = SSCE_OpenSession();
|
||
SpellTicks = TickCount();
|
||
if (SpellSession>=0)
|
||
{
|
||
SpellUserDict = SpellUserADict = -1;
|
||
|
||
// Eudora Folder:Eudora Items:
|
||
mySpec.vRefNum = ItemsFolder.vRef;
|
||
mySpec.parID = ItemsFolder.dirId;
|
||
*mySpec.name = 0;
|
||
if (!SubFolderSpecOf(&mySpec,SPELL_DICTS,false,&mySpec))
|
||
howMany += SpellOpenLexDir(&mySpec);
|
||
|
||
// Eudora Folder:
|
||
mySpec.vRefNum = Root.vRef;
|
||
mySpec.parID = Root.dirId;
|
||
*mySpec.name = 0;
|
||
if (!SubFolderSpecOf(&mySpec,SPELL_DICTS,false,&mySpec))
|
||
howMany += SpellOpenLexDir(&mySpec);
|
||
|
||
// Application Support:Eudora:
|
||
while (n-->0)
|
||
if (!FindSubFolderSpec(domains[n],kApplicationSupportFolderType,EUDORA_EUDORA,false,&mySpec))
|
||
if (!SameSpec(&mySpec,&oldSpec))
|
||
{
|
||
oldSpec = mySpec;
|
||
if (!SubFolderSpecOf(&mySpec,SPELL_DICTS,false,&mySpec))
|
||
howMany += SpellOpenLexDir(&mySpec);
|
||
}
|
||
|
||
// Eudora Stuff:
|
||
if (!StuffFolderSpec(&mySpec))
|
||
if (!SubFolderSpecOf(&mySpec,SPELL_DICTS,false,&mySpec))
|
||
howMany += SpellOpenLexDir(&mySpec);
|
||
|
||
// Do we need to make the user dictionaries?
|
||
if (SpellUserDict<0 || SpellUserADict<0)
|
||
{
|
||
mySpec.vRefNum = ItemsFolder.vRef;
|
||
mySpec.parID = ItemsFolder.dirId;
|
||
*mySpec.name = 0;
|
||
if (!SubFolderSpecOf(&mySpec,SPELL_DICTS,true,&mySpec))
|
||
{
|
||
if (SpellUserDict<0)
|
||
{
|
||
SpellMakeUserDict(&mySpec,SPELL_UDICT);
|
||
GetRString(mySpec.name,SPELL_UDICT);
|
||
if (!SpellOpenLex(&mySpec)) howMany++;
|
||
}
|
||
if (SpellUserADict<0)
|
||
{
|
||
SpellMakeUserDict(&mySpec,SPELL_UADICT);
|
||
GetRString(mySpec.name,SPELL_UADICT);
|
||
if (!SpellOpenLex(&mySpec)) howMany++;
|
||
}
|
||
}
|
||
}
|
||
|
||
// when counting how many dictionaries we got, don't
|
||
// count the user dictionaries, because without any
|
||
// real dictionaries, they're useless
|
||
if (SpellUserDict>=0) howMany--;
|
||
if (SpellUserADict>=0) howMany--;
|
||
}
|
||
if (!howMany) SpellClose(true);
|
||
else WinterTreeOptions = GetPrefLong(PREF_WINTERTREE_OPTS);
|
||
}
|
||
SpellTicks = TickCount();
|
||
return(SpellSession);
|
||
}
|
||
|
||
/************************************************************************
|
||
* SpellOpenLexDir - open all the dictionaries in a folder; returns number opened
|
||
************************************************************************/
|
||
short SpellOpenLexDir(FSSpecPtr inSpec)
|
||
{
|
||
CInfoPBRec hfi;
|
||
FSSpec localSpec = *inSpec;
|
||
short howMany = 0;
|
||
|
||
Zero(hfi);
|
||
hfi.hFileInfo.ioNamePtr = localSpec.name;
|
||
while (!DirIterate(localSpec.vRefNum,localSpec.parID,&hfi))
|
||
if (!SpellOpenLex(&localSpec)) howMany++;
|
||
|
||
return howMany;
|
||
}
|
||
/************************************************************************
|
||
* SpellMakeUserDict - create the user dictionaries if they don't exist
|
||
************************************************************************/
|
||
OSErr SpellMakeUserDict(FSSpecPtr dirSpec,short id)
|
||
{
|
||
FSSpec spec;
|
||
OSErr err;
|
||
Str255 userContents;
|
||
|
||
spec = *dirSpec;
|
||
|
||
GetRString(spec.name,id);
|
||
if (!(err=FSpCreate(&spec,SPELLER_CREATOR,'TEXT',smSystemScript)))
|
||
{
|
||
GetRString(userContents,id+2);
|
||
err = BlatPtr(&spec,userContents+1,*userContents,false);
|
||
}
|
||
return(err);
|
||
}
|
||
|
||
|
||
/************************************************************************
|
||
* SpellOpenLex - open a lexicon
|
||
************************************************************************/
|
||
OSErr SpellOpenLex(FSSpecPtr spec)
|
||
{
|
||
Str255 path;
|
||
FSSpec localSpec;
|
||
Str31 name;
|
||
OSErr err = noErr;
|
||
Boolean canWrite;
|
||
short id;
|
||
|
||
if (!HaveSpeller()) return(fnfErr);
|
||
|
||
PCopy(path,spec->name);
|
||
localSpec = *spec;
|
||
do
|
||
{
|
||
GetDirName(nil,localSpec.vRefNum,localSpec.parID,name);
|
||
PCatC(name,':');
|
||
PInsert(path,sizeof(path),name,path+1);
|
||
if (localSpec.parID==2) break;
|
||
} while (!(err=ParentSpec(&localSpec,&localSpec)));
|
||
P2CStr(path);
|
||
if (!err)
|
||
{
|
||
id = SSCE_OpenLex(SpellSession,path,GetRLong(SPELL_MEM_LIMIT));
|
||
if (id<0) err = id;
|
||
}
|
||
if (!err)
|
||
{
|
||
if (SpellUserDict<0 && EqualStrRes(spec->name,SPELL_UDICT))
|
||
{
|
||
CanWrite(spec,&canWrite);
|
||
if (canWrite) SpellUserDict = id;
|
||
}
|
||
else if (SpellUserADict<0 && EqualStrRes(spec->name,SPELL_UADICT))
|
||
{
|
||
CanWrite(spec,&canWrite);
|
||
if (canWrite) SpellUserADict = id;
|
||
}
|
||
}
|
||
return(err);
|
||
}
|
||
|
||
/************************************************************************
|
||
* SpellAgain - respell with new options
|
||
************************************************************************/
|
||
void SpellAgain(void)
|
||
{
|
||
WindowPtr winWP;
|
||
MyWindowPtr win;
|
||
PETEHandle pte;
|
||
|
||
EnableSpellMenu(true);
|
||
for (winWP=FrontWindow_();winWP;winWP=GetNextWindow(winWP))
|
||
if (IsKnownWindowMyWindow(winWP) && (win=GetWindowMyWindowPtr(winWP)))
|
||
for (pte=win->pteList;pte;pte=PeteNext(pte))
|
||
if (!SpellDisabled(pte))
|
||
(*PeteExtra(pte))->spelled = 0;
|
||
}
|
||
|
||
/************************************************************************
|
||
* SpellScan - scan msgs for misspellings and if found style
|
||
************************************************************************/
|
||
void SpellScan(void)
|
||
{
|
||
WindowPtr winWP;
|
||
MyWindowPtr win;
|
||
PETEHandle pte;
|
||
Boolean quoteScanning = AmQuoteScanning();
|
||
Boolean scanned = false;
|
||
|
||
if (!HaveSpeller()) return;
|
||
|
||
for (winWP=FrontWindow_();winWP;winWP=GetNextWindow(winWP))
|
||
{
|
||
if (IsKnownWindowMyWindow(winWP))
|
||
{
|
||
win = GetWindowMyWindowPtr (winWP);
|
||
for (pte=win->pteList;pte;pte=PeteNext(pte))
|
||
{
|
||
while ((*PeteExtra(pte))->spelled >= 0 && (!quoteScanning||(*PeteExtra(pte))->quoteScanned==-1) && (*PeteExtra(pte))->urlScanned==-1)
|
||
{
|
||
UsingWindow(winWP);
|
||
PeteSpellScan(pte,false);
|
||
scanned = true;
|
||
if (NEED_YIELD) {
|
||
UseFeature (featureSpellChecking);
|
||
return;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
if (scanned)
|
||
UseFeature (featureSpellChecking);
|
||
}
|
||
|
||
#define SpellFirstWordChar(x) (IsWordChar[x] || isdigit(x))
|
||
#define SpellMidWordChar(x) (IsWordChar[x] || isdigit(x))
|
||
#define SpellSingleOK(x) ((x)==(Byte)'-'||(x)==(Byte)'<27>'||(x)=='\''||(x)==(Byte)'<27>')
|
||
#define SpellSingleFunnyOK(x) ((x)=='@'||(x=='.'))
|
||
#define SpellWordChar(x) (SpellMidWordChar(x)||SpellSingleOK(x))
|
||
#define kSpellUnitMask (pURLLabel|pLinkLabel|pQuoteLabel)
|
||
|
||
/************************************************************************
|
||
* PeteSpellScan - scan a pete record for misspellings
|
||
************************************************************************/
|
||
void PeteSpellScan(PETEHandle pte,Boolean manual)
|
||
{
|
||
Handle text;
|
||
long plainStart = (*PeteExtra(pte))->spelled;
|
||
long plainEnd = plainStart;
|
||
Boolean peteDirtyWas = PeteIsDirty(pte)!=0;
|
||
Boolean winDirtyWas = (*PeteExtra(pte))->win->isDirty;
|
||
PETEStyleEntry pse;
|
||
long lEnd;
|
||
UPtr spot;
|
||
UPtr end;
|
||
long len;
|
||
Byte word[SSCE_MAX_WORD_LEN+2];
|
||
Byte replace[SSCE_MAX_WORD_LEN+2];
|
||
Boolean ok;
|
||
long selStart,selEnd;
|
||
Boolean iFoundAFunny;
|
||
Boolean isWord;
|
||
HeadSpec hs;
|
||
Str63 frogChars;
|
||
|
||
if (!HaveSpeller()) return;
|
||
|
||
// done?
|
||
if (SpellChecked(pte)) return;
|
||
if (0>SpellOpen()) return;
|
||
Zero(pse);
|
||
*replace = 0;
|
||
|
||
// pre-checks; shd probably be farmed out to gonnaShow or something, but...
|
||
len=PETEGetTextLen(PETE,pte);
|
||
if (GetWindowKind(GetMyWindowWindowPtr((*PeteExtra(pte))->win))==COMP_WIN)
|
||
{
|
||
CompHeadFind(Win2MessH((*PeteExtra(pte))->win),SUBJ_HEAD,&hs);
|
||
lEnd = hs.value;
|
||
if (plainStart<lEnd)
|
||
plainStart = plainEnd = lEnd;
|
||
else if (plainStart>=hs.stop)
|
||
{
|
||
CompHeadFind(Win2MessH((*PeteExtra(pte))->win),0,&hs);
|
||
lEnd = hs.value;
|
||
if (plainStart<lEnd)
|
||
plainStart = plainEnd = lEnd;
|
||
}
|
||
}
|
||
if (plainStart >= len)
|
||
{
|
||
(*PeteExtra(pte))->spelled = sprSpellComplete; // don't scan again
|
||
}
|
||
if ((*PeteExtra(pte))->spelled<0) return;
|
||
|
||
// ok, actual spelling now
|
||
NoScannerResets++; // these changes don't count
|
||
PETEGetStyle(PETE,pte,plainStart,nil,&pse);
|
||
pse.psStyle.textStyle.tsLabel &= ~pStationeryLabel;
|
||
if (PeteIsExcerptAt(pte,plainStart))
|
||
{
|
||
PeteParaRange(pte,&plainStart,&plainEnd);
|
||
}
|
||
else if (pse.psStyle.textStyle.tsLabel&kSpellUnitMask)
|
||
{
|
||
PETEFindLabelRun(PETE,pte,plainStart,&plainStart,&plainEnd,pse.psStyle.textStyle.tsLabel&kSpellUnitMask,pse.psStyle.textStyle.tsLabel&kSpellUnitMask);
|
||
}
|
||
else
|
||
{
|
||
PeteGetTextAndSelection(pte,&text,&selStart,&selEnd);
|
||
spot = *text+plainStart;
|
||
|
||
// the starting spot might be in the middle of something. Check.
|
||
if (pse.psStyle.textStyle.tsLabel==pSpellLabel)
|
||
PETEFindLabelRun(PETE,pte,plainStart,&plainStart,&lEnd,pSpellLabel,pSpellLabel);
|
||
else if ((*PeteExtra(pte))->spellReset)
|
||
{
|
||
while (spot>*text && !IsLWSP(spot[-1])) spot--;
|
||
plainStart = spot-*text;
|
||
}
|
||
(*PeteExtra(pte))->spellReset = false;
|
||
|
||
// reinit in case they changed
|
||
PeteGetTextAndSelection(pte,&text,&selStart,&selEnd);
|
||
spot = *text+plainStart;
|
||
end = *text+len;
|
||
|
||
isWord = SpellFirstWordChar(*spot);
|
||
spot++;
|
||
|
||
if (!isWord)
|
||
{
|
||
while (spot<end && !SpellFirstWordChar(*spot)) spot++;
|
||
plainEnd = spot-*text;
|
||
}
|
||
else
|
||
{
|
||
iFoundAFunny = false;
|
||
for (;;)
|
||
{
|
||
while (spot<end && SpellMidWordChar(*spot)) spot++;
|
||
if (spot<end-1 && IsWordChar[spot[1]])
|
||
{
|
||
if (SpellSingleFunnyOK(*spot)) // foo@bar, foo.bar
|
||
{
|
||
iFoundAFunny = true;
|
||
spot++;
|
||
}
|
||
else if (SpellSingleOK(*spot)) // foo-bar, foo'bar, etc
|
||
{
|
||
// Special case for french contractions; c'est la vie, par example
|
||
if (*spot=='\'')
|
||
{
|
||
Byte theChar = spot[1];
|
||
LowercaseText(&theChar,1,smSystemScript);
|
||
if (PIndex(GetRString(frogChars,FROG_CHARS),theChar))
|
||
break;
|
||
}
|
||
spot++;
|
||
}
|
||
else
|
||
break;
|
||
}
|
||
else
|
||
break;
|
||
}
|
||
|
||
plainEnd = spot-*text;
|
||
if (iFoundAFunny)
|
||
/*nevermind*/;
|
||
else
|
||
{
|
||
ok = true;
|
||
if (plainEnd-plainStart<sizeof(word)-2)
|
||
{
|
||
MakePStr(word,*text+plainStart,plainEnd-plainStart);
|
||
ok = SpellWordKnown(word,replace);
|
||
}
|
||
|
||
if (!ok && (PeteIsGraphicAt(pte,plainStart) || PeteIsGraphicAt(pte,plainEnd)))
|
||
ok = true; // avoid graphics
|
||
|
||
if (!ok)
|
||
{
|
||
if (*replace && !PETESelectionLocked(PETE,pte,plainStart,plainEnd))
|
||
{
|
||
long oldSelStart, oldSelEnd;
|
||
Boolean selWasInWord, selWasWord;
|
||
|
||
PeteGetTextAndSelection(pte,nil,&oldSelStart,&oldSelEnd);
|
||
selWasInWord = oldSelStart>=plainStart && oldSelEnd <= plainEnd;
|
||
selWasWord = oldSelStart==plainStart && oldSelEnd == plainEnd;
|
||
|
||
if (!PeteDelete(pte,plainStart,plainEnd))
|
||
{
|
||
plainEnd = plainStart;
|
||
if (!PeteInsertPtr(pte,plainStart,replace+1,*replace))
|
||
{
|
||
plainEnd = plainStart = plainStart+*replace;
|
||
// restore selection
|
||
if (selWasWord) PeteSelect(nil,pte,plainStart,plainEnd);
|
||
else if (selWasInWord) PeteSelect(nil,pte,plainEnd,plainEnd);
|
||
}
|
||
}
|
||
}
|
||
else
|
||
{
|
||
PeteLabel(pte,plainStart,plainEnd,pSpellLabel,pSpellLabel);
|
||
plainStart = plainEnd;
|
||
// mark the character after this one as not misspelled, otherwise
|
||
// we'll scan over and over and over again in certain sitches, eg,
|
||
// xxx-rat -> xxx- rat
|
||
if (plainEnd<len) plainEnd++;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
if (plainStart<plainEnd) PeteNoLabel(pte,plainStart,plainEnd,pSpellLabel);
|
||
if (!*replace)
|
||
{
|
||
if (!peteDirtyWas) PETEMarkDocDirty(PETE,pte,False);
|
||
(*PeteExtra(pte))->win->isDirty = winDirtyWas;
|
||
}
|
||
(*PeteExtra(pte))->spelled = plainEnd;
|
||
NoScannerResets--;
|
||
SpellClose(false);
|
||
}
|
||
|
||
/************************************************************************
|
||
* SpellWordKnown - do we know this word?
|
||
************************************************************************/
|
||
Boolean SpellWordKnown(PStr word,PStr replace)
|
||
{
|
||
Str255 localWord;
|
||
Str63 goAway;
|
||
short result;
|
||
UPtr spot;
|
||
Boolean allJunk = true;
|
||
|
||
if (!HaveSpeller()) return(true);
|
||
|
||
for (spot=word+1;spot<=word+*word;spot++)
|
||
if (IsWordChar[*spot])
|
||
{
|
||
allJunk = false;
|
||
break;
|
||
}
|
||
|
||
if (allJunk) return true;
|
||
|
||
PtoCcpy(localWord,word);
|
||
TransLitRes(localWord+1,*localWord,ktFlatten);
|
||
result = SSCE_CheckWord(SpellSession,WinterTreeOptions&0xfff,localWord,goAway,sizeof(goAway));
|
||
if ((result&SSCE_CHANGE_WORD_RSLT) && replace)
|
||
{
|
||
C2PStr(goAway);
|
||
PCopy(replace,goAway);
|
||
}
|
||
|
||
SpellTicks = TickCount();
|
||
return(!result);
|
||
}
|
||
|
||
/************************************************************************
|
||
* SpellClose - close a spelling session
|
||
************************************************************************/
|
||
void SpellClose(Boolean withPrejuidice)
|
||
{
|
||
if (SpellSession>=0)
|
||
{
|
||
if (SpellInUse) SpellInUse--;
|
||
if ((!SpellInUse && TickCount()-SpellTicks>60*GetRLong(SPELL_HOLD_OPEN_SECS)) || withPrejuidice)
|
||
{
|
||
SSCE_CloseSession(SpellSession);
|
||
SpellSession = -1;
|
||
}
|
||
}
|
||
}
|
||
|
||
/************************************************************************
|
||
* SpellMenu - handle a selection from the spellchecking menu
|
||
************************************************************************/
|
||
void SpellMenu(short item,short modifiers)
|
||
{
|
||
MyWindowPtr win = GetWindowMyWindowPtr(FrontWindow_());
|
||
Str63 replace;
|
||
long sel;
|
||
|
||
if (!HaveSpeller()) return;
|
||
if (win && win->pte)
|
||
{
|
||
switch(item)
|
||
{
|
||
case spellCheckItem:
|
||
PeteGetTextAndSelection(win->pte,nil,&sel,nil);
|
||
PeteSetURLRescan(win->pte,sel);
|
||
{
|
||
SpellOpen();
|
||
if (SpellInUse)
|
||
{
|
||
(*PeteExtra(win->pte))->spelled = 0;
|
||
do
|
||
{
|
||
PeteSpellScan(win->pte,true);
|
||
CycleBalls();
|
||
}
|
||
while (!CommandPeriod && (*PeteExtra(win->pte))->spelled>=0);
|
||
SpellClose(false);
|
||
}
|
||
}
|
||
break;
|
||
case spellNextItem:
|
||
if (!SpellChecked(win->pte))
|
||
SpellMenu(spellCheckItem,0);
|
||
SpellNext(win->pte);
|
||
break;
|
||
case spellAddItem:
|
||
SpellAdd(win->pte);
|
||
break;
|
||
case spellRemoveItem:
|
||
SpellRemove(win->pte);
|
||
break;
|
||
default:
|
||
SpellReplace(win->pte,MyGetItem(GetMHandle(SPELL_HIER_MENU),item,replace));
|
||
break;
|
||
}
|
||
}
|
||
SpellTicks = TickCount();
|
||
}
|
||
|
||
/************************************************************************
|
||
* SpellReplace - replace the current word
|
||
************************************************************************/
|
||
OSErr SpellReplace(PETEHandle pte,PStr replaceMe)
|
||
{
|
||
long selStart=0,selEnd=0;
|
||
OSErr err = noErr;
|
||
Boolean selection;
|
||
|
||
PeteGetTextAndSelection(pte,nil,&selStart,&selEnd);
|
||
selection = selStart!=selEnd;
|
||
SpellGetCurrentWord(nil,nil,nil,nil,&selStart,&selEnd);
|
||
if (selStart!=selEnd)
|
||
{
|
||
PeteSetURLRescan(pte,selStart);
|
||
PeteCalcOff(pte);
|
||
PetePrepareUndo(pte,peUndoPaste,selStart,selEnd,nil,nil);
|
||
PeteSelect(nil,pte,selStart,selEnd);
|
||
if (!(err = PETEInsertTextPtr(PETE,pte,-1,replaceMe+1,*replaceMe,nil)))
|
||
{
|
||
PeteSelect(nil,pte,selection ? selStart:selStart+*replaceMe,selStart+*replaceMe);
|
||
PeteFinishUndo(pte,peUndoPaste,selStart,selStart+*replaceMe);
|
||
}
|
||
if (err) PeteKillUndo(pte);
|
||
PeteCalcOn(pte);
|
||
}
|
||
SpellTicks = TickCount();
|
||
return err;
|
||
}
|
||
|
||
/************************************************************************
|
||
* SpellNext - find the next misspelling
|
||
************************************************************************/
|
||
OSErr SpellNext(PETEHandle pte)
|
||
{
|
||
long selStart, selEnd;
|
||
PETEStyleEntry pse;
|
||
long firstStart = -1;
|
||
|
||
PeteGetTextAndSelection(pte,nil,nil,&selEnd);
|
||
PeteStyleAt(pte,selEnd,&pse);
|
||
if (pse.psStyle.textStyle.tsLabel&pSpellLabel)
|
||
PETEFindLabelRun(PETE,pte,0,&firstStart,&selEnd,pSpellLabel,pSpellLabel);
|
||
if (!PETEFindLabelRun(PETE,pte,selEnd,&selStart,&selEnd,pSpellLabel,pSpellLabel) ||
|
||
!PETEFindLabelRun(PETE,pte,0,&selStart,&selEnd,pSpellLabel,pSpellLabel) && firstStart!=selStart)
|
||
{
|
||
PeteSelect(nil,pte,selStart,selEnd);
|
||
PETEScroll(PETE,pte,0,pseCenterSelection);
|
||
return(noErr);
|
||
}
|
||
else
|
||
{
|
||
SysBeep(20L);
|
||
return(fnfErr);
|
||
}
|
||
SpellTicks = TickCount();
|
||
}
|
||
|
||
/************************************************************************
|
||
* SpellAdd - add the current word
|
||
************************************************************************/
|
||
OSErr SpellAdd(PETEHandle pte)
|
||
{
|
||
OSErr err = fnfErr;
|
||
Str63 word;
|
||
long start,end;
|
||
Boolean peteDirtyWas = PeteIsDirty(pte)!=0;
|
||
Boolean winDirtyWas = (*PeteExtra(pte))->win->isDirty;
|
||
|
||
if (!HaveSpeller()) return(fnfErr);
|
||
|
||
if (SpellOpen()>=0)
|
||
{
|
||
SpellGetCurrentWord(word,nil,nil,nil,&start,&end);
|
||
if (*word)
|
||
{
|
||
P2CStr(word);
|
||
if (SpellUserADict>=0)
|
||
err = SSCE_DelFromLex(SpellSession,SpellUserADict,word);
|
||
if (err)
|
||
err = SSCE_AddToLex(SpellSession,SpellUserDict,word,nil);
|
||
}
|
||
PeteNoLabel(pte,start,end,pSpellLabel);
|
||
if (!peteDirtyWas) PETEMarkDocDirty(PETE,pte,False);
|
||
(*PeteExtra(pte))->win->isDirty = winDirtyWas;
|
||
SpellAgain();
|
||
SpellClose(false);
|
||
}
|
||
SpellTicks = TickCount();
|
||
return(err);
|
||
}
|
||
|
||
/************************************************************************
|
||
* SpellRemove - remove the current word
|
||
************************************************************************/
|
||
OSErr SpellRemove(PETEHandle pte)
|
||
{
|
||
OSErr err = fnfErr;
|
||
Str63 word;
|
||
long start,end;
|
||
Boolean peteDirtyWas = PeteIsDirty(pte)!=0;
|
||
Boolean winDirtyWas = (*PeteExtra(pte))->win->isDirty;
|
||
|
||
if (!HaveSpeller()) return(fnfErr);
|
||
|
||
if (SpellOpen()>=0)
|
||
{
|
||
SpellGetCurrentWord(word,nil,nil,nil,&start,&end);
|
||
if (*word)
|
||
{
|
||
P2CStr(word);
|
||
if (SpellUserDict>=0)
|
||
err = SSCE_DelFromLex(SpellSession,SpellUserDict,word);
|
||
if (err)
|
||
err = SSCE_AddToLex(SpellSession,SpellUserADict,word,nil);
|
||
}
|
||
PeteLabel(pte,start,end,pSpellLabel,pSpellLabel);
|
||
if (!peteDirtyWas) PETEMarkDocDirty(PETE,pte,False);
|
||
(*PeteExtra(pte))->win->isDirty = winDirtyWas;
|
||
SpellAgain();
|
||
SpellClose(false);
|
||
}
|
||
SpellTicks = TickCount();
|
||
return(err);
|
||
}
|
||
|
||
/************************************************************************
|
||
* EnableSpellMenu - enable the spell-checking menu items
|
||
************************************************************************/
|
||
void EnableSpellMenu(Boolean all)
|
||
{
|
||
static Str63 lastWord;
|
||
Str63 word;
|
||
Boolean checked=false,writeable=false,misspelled=false;
|
||
MenuHandle mh = GetMHandle(SPELL_HIER_MENU);
|
||
UHandle suggestions=nil;
|
||
short i;
|
||
short len;
|
||
Boolean same;
|
||
WindowPtr winWP = FrontWindow_();
|
||
MyWindowPtr win = GetWindowMyWindowPtr (winWP);
|
||
Boolean have = HaveSpeller();
|
||
short item;
|
||
static wordCanAdd;
|
||
static wordCanRemove;
|
||
static short key;
|
||
static Boolean deferSuggestions;
|
||
|
||
|
||
if (mh)
|
||
{
|
||
// diddle the add command
|
||
GetItemCmd(mh,spellCheckItem,&key);
|
||
if (key)
|
||
{
|
||
SetItemCmd(mh,spellNextItem,key);
|
||
SetMenuItemModifiers(mh,spellNextItem,kMenuOptionModifier);
|
||
SetItemCmd(mh,spellAddItem,key);
|
||
SetMenuItemModifiers(mh,spellAddItem,kMenuShiftModifier+kMenuOptionModifier);
|
||
}
|
||
|
||
if (!all && have) SpellGetCurrentWord(word,&checked,&writeable,&misspelled,nil,nil);
|
||
else *word = 0;
|
||
|
||
same = EqualString(word,lastWord,true,true);
|
||
|
||
PCopy(lastWord,word);
|
||
|
||
if (!same) {wordCanAdd = wordCanRemove = 0;TrashMenu(mh,spellItemLimit);}
|
||
|
||
if (SpellOptGuesses && (!same||deferSuggestions) && *word && misspelled)
|
||
{
|
||
if (MainEvent.what!=mouseDown)
|
||
deferSuggestions = true;
|
||
else
|
||
{
|
||
deferSuggestions = false;
|
||
PCopy(lastWord,word);
|
||
SpellSuggestions(word,&suggestions);
|
||
if (suggestions)
|
||
{
|
||
item = CountMenuItems(mh);
|
||
AppendMenu(mh,"\p-");
|
||
DisableItem(mh,++item);
|
||
len = GetHandleSize(suggestions);
|
||
for (i=0;i<len;i+=(*suggestions)[i]+1)
|
||
{
|
||
PSCopy(word,(*suggestions)+i);
|
||
MyAppendMenu(mh,word);
|
||
item++;
|
||
EnableIf(mh,item,writeable);
|
||
}
|
||
ZapHandle(suggestions);
|
||
}
|
||
}
|
||
}
|
||
|
||
if (!same && *word)
|
||
{
|
||
wordCanAdd = SpellUserDict>=0&&misspelled;
|
||
wordCanRemove = (SpellUserDict>=0 || SpellUserADict>=0)&&*word&&checked&&!misspelled;
|
||
}
|
||
|
||
EnableIf(mh,spellCheckItem,all||have&&IsMyWindow(winWP)&&win&&PeteIsValid(win->pte)&&!SpelledAuto(win->pte));
|
||
EnableIf(mh,spellNextItem,all||have&&IsMyWindow(winWP)&&win&&PeteIsValid(win->pte));
|
||
EnableIf(mh,spellAddItem,all||wordCanAdd);
|
||
EnableIf(mh,spellRemoveItem,all||wordCanRemove);
|
||
}
|
||
SpellTicks = TickCount();
|
||
}
|
||
|
||
/************************************************************************
|
||
* SpellGetCurrentWord - get the current word
|
||
************************************************************************/
|
||
void SpellGetCurrentWord(PStr wordPtr,Boolean *checked,Boolean *writeable, Boolean *misspelled,long *start,long *stop)
|
||
{
|
||
WindowPtr winWP = FrontWindow_();
|
||
MyWindowPtr win = GetWindowMyWindowPtr(winWP);
|
||
PETEHandle pte = win && IsMyWindow(winWP) ? win->pte : nil;
|
||
long selStart,selEnd;
|
||
UPtr spot;
|
||
UHandle text;
|
||
PETEStyleEntry pse;
|
||
Str63 word;
|
||
|
||
if (wordPtr) *wordPtr = 0;
|
||
if (checked) *checked = false;
|
||
if (writeable) *writeable = false;
|
||
if (misspelled) *misspelled = false;
|
||
|
||
// anyone home?
|
||
if (!PeteIsValid(pte) || !HaveSpeller()) return;
|
||
|
||
// this is easy...
|
||
if (writeable) *writeable = !PETESelectionLocked(PETE,pte,-1,-1);
|
||
if (checked) *checked = SpellChecked(pte);
|
||
|
||
// don't bother unless the user has checked spelling
|
||
if (SpellDisabled(pte)) return;
|
||
|
||
// Ok, the user has done his part. Our turn.
|
||
PeteGetTextAndSelection(pte,&text,&selStart,&selEnd);
|
||
|
||
// too big?
|
||
if (selEnd-selStart>63) return;
|
||
|
||
// user-selected word?
|
||
if (selEnd>selStart)
|
||
{
|
||
MakePStr(word,*text+selStart,selEnd-selStart);
|
||
for (spot=word+*word;spot>word;spot--)
|
||
{
|
||
if (!SpellWordChar(*spot))
|
||
{
|
||
// user has crap selected. Make it go away.
|
||
return;
|
||
}
|
||
}
|
||
|
||
// ok, the user has a valid word selected. Do we think it misspelled?
|
||
PeteStyleAt(pte,(selStart+selEnd)/2,&pse);
|
||
if (misspelled) *misspelled = (pse.psStyle.textStyle.tsLabel&pSpellLabel) != 0;
|
||
if (wordPtr) PCopy(wordPtr,word);
|
||
}
|
||
else
|
||
{
|
||
// is insertion point in misspelled word?
|
||
PeteStyleAt(pte,-1,&pse);
|
||
if ((pse.psStyle.textStyle.tsLabel&pSpellLabel) != 0)
|
||
{
|
||
if (misspelled) *misspelled = true;
|
||
PETEFindLabelRun(PETE,pte,selStart-1,&selStart,&selEnd,pSpellLabel,pSpellLabel);
|
||
if (wordPtr)
|
||
{
|
||
MakePStr(word,*text+selStart,selEnd-selStart);
|
||
PCopy(wordPtr,word);
|
||
}
|
||
}
|
||
}
|
||
if (start) *start = selStart;
|
||
if (stop) *stop = selEnd;
|
||
}
|
||
|
||
/************************************************************************
|
||
* SpellSuggestions - give the user some suggestions
|
||
************************************************************************/
|
||
void SpellSuggestions(PStr word,UHandle *suggestions)
|
||
{
|
||
Str63 local;
|
||
Accumulator a;
|
||
Ptr crappySuggestPtr;
|
||
UPtr spot;
|
||
short scores[20];
|
||
long topScore;
|
||
short *score;
|
||
|
||
if (!HaveSpeller()) return;
|
||
|
||
SpellOpen();
|
||
if (SpellInUse)
|
||
{
|
||
crappySuggestPtr = NuPtr(4 K);
|
||
AccuInit(&a);
|
||
*suggestions = nil;
|
||
|
||
if (WinterTreeOptions&(1L<<20))
|
||
{
|
||
WinterTreeOptions |= SSCE_SUGGEST_PHONETIC_OPT;
|
||
WinterTreeOptions &= ~SSCE_SUGGEST_TYPOGRAPHICAL_OPT;
|
||
}
|
||
|
||
SSCE_SetOption(SpellSession,SSCE_SUGGEST_PHONETIC_OPT,0!=(WinterTreeOptions&SSCE_SUGGEST_PHONETIC_OPT));
|
||
SSCE_SetOption(SpellSession,SSCE_SUGGEST_TYPOGRAPHICAL_OPT,0==(WinterTreeOptions&SSCE_SUGGEST_TYPOGRAPHICAL_OPT));
|
||
|
||
if (crappySuggestPtr)
|
||
{
|
||
PCopy(local,word);
|
||
P2CStr(local);
|
||
if (!SSCE_Suggest(SpellSession,
|
||
local,
|
||
GetRLong(SPELL_SUGGEST_DEPTH),
|
||
crappySuggestPtr, 4 K,
|
||
scores,20))
|
||
{
|
||
topScore = scores[0]*16;
|
||
for (score=scores,spot=crappySuggestPtr;*spot;score++,spot+=*local+1)
|
||
{
|
||
if ((*score)*20<topScore) break;
|
||
CtoPCpy(local,spot);
|
||
AccuAddPtr(&a,local,*local+1);
|
||
}
|
||
AccuTrim(&a);
|
||
*suggestions = a.data;
|
||
a.data = nil;
|
||
}
|
||
}
|
||
|
||
if (crappySuggestPtr) ZapPtr(crappySuggestPtr);
|
||
AccuZap(a);
|
||
SpellClose(false);
|
||
}
|
||
SpellTicks = TickCount();
|
||
}
|
||
|
||
/************************************************************************
|
||
* SpellAnyWrongHuh - figure out if anything is misspelled
|
||
************************************************************************/
|
||
Boolean SpellAnyWrongHuh(PETEHandle pte)
|
||
{
|
||
long oldSpelled;
|
||
Boolean result = false;
|
||
long selStart, selEnd;
|
||
|
||
if (0<=SpellOpen())
|
||
{
|
||
if ((*PeteExtra(pte))->spelled!=sprSpellComplete)
|
||
{
|
||
oldSpelled = (*PeteExtra(pte))->spelled;
|
||
if ((*PeteExtra(pte))->spelled<0) (*PeteExtra(pte))->spelled = 0;
|
||
while (!CommandPeriod && (*PeteExtra(pte))->spelled!=sprSpellComplete)
|
||
{
|
||
CycleBalls();
|
||
PeteSpellScan(pte,true);
|
||
}
|
||
}
|
||
|
||
if (!CommandPeriod)
|
||
result = !PETEFindLabelRun(PETE,pte,0,&selStart,&selEnd,pSpellLabel,pSpellLabel);
|
||
SpellClose(false);
|
||
}
|
||
|
||
return(result);
|
||
}
|
||
|
||
/************************************************************************
|
||
* AppendSpellItems - add spell items to context menu
|
||
************************************************************************/
|
||
OSErr AppendSpellItems(PETEHandle pte,MenuHandle contextMenu)
|
||
{
|
||
MenuHandle mh;
|
||
short i;
|
||
short n;
|
||
Boolean bar1, bar2;
|
||
|
||
if (!HaveSpeller()) return noErr;
|
||
|
||
if (SpellDisabled(pte)) return noErr; // don't bother unless the user has checked spelling
|
||
|
||
EnableSpellMenu(false);
|
||
|
||
if (mh = GetMHandle(SPELL_HIER_MENU))
|
||
{
|
||
n = CountMenuItems(mh);
|
||
bar2 = n>spellItemLimit;
|
||
bar1 = false;
|
||
for (i=spellBar1Item;i<spellItemLimit;i++)
|
||
if (IsEnabled(GetMenuID(mh),i)) {bar1 = true; break;}
|
||
|
||
for (i=spellCheckItem+1;i<=n;i++)
|
||
{
|
||
if (i==spellBar1Item&&bar1 || i==spellItemLimit&&bar2)
|
||
AppendMenu(contextMenu,"\p-"); // add a divider
|
||
else if (IsEnabled(GetMenuID(mh),i))
|
||
{
|
||
CopyMenuItem(mh,i,contextMenu,REAL_BIG);
|
||
SetMenuItemCommandID(contextMenu,CountMenuItems(contextMenu),(GetMenuID(mh)<<16)|i);
|
||
}
|
||
}
|
||
}
|
||
return(noErr);
|
||
}
|
||
|
||
#endif
|
||
|
||
/************************************************************************
|
||
* HaveSpeller - is the speller installed?
|
||
************************************************************************/
|
||
Boolean HaveSpeller(void)
|
||
{
|
||
#ifdef WINTERTREE
|
||
return(HasFeature (featureSpellChecking) && (void*)SSCE_OpenSession!=(void*)kUnresolvedCFragSymbolAddress);
|
||
#else
|
||
return(false);
|
||
#endif
|
||
}
|