In the first 2 parts of this thread, I discussed how to spell check using AppleScript and then how to transfer that to Revolution. This time, I want to talk about the design of the spell checker inside Revolution. The main questions I felt that I had to answer were:
- How should the user be notified of spelling errors?
- Should checking happen all the time or only when the user specifically asks for it?
With regard to notification, the standard these days seems to be a red line of some sort under the incorrect words. Microsoft uses a wavy line, Apple uses a dotted line. However neither of these is easily accessible in Revolution, so I decided to use a text style and colour to indicate spelling errors. The key is to choose a style and a colour that you are unlikely to use normally. Otherwise, spelling errors may just look like normal text.
I experimented with several different options but in the end I went for red underlined text to indicate errors. This will be a matter of choice and will depend on the sort of application you are creating, but this is what works for me.
Here is a screen shot of a portion of my application, showing several words marked in this way.
Before I move on to scripting this, the next decision needs to be made: when to check?
Again, this is a matter for opinion and I guess it should really be made an option in your application's preferences. However I prefer checking as I type. Obviously, there is no point in checking after every keystroke as that would make incomplete words appear as errors. So I decided to trigger the spell check using a rawKeyUp handler. A rawKeyUp or rawKeyDown event handler has a single parameter which is a numeric code for the pressed key. If you want to test out some keys, I suggest you try my
KeyCoder stack which will tell you the code for any key you press.
I decided to check for Space, Return, Enter, Tab, Backspace, Delete, Period & Comma. After any of these were clicked, I would then initiate a spell check.
This worked well, but as there got to be more & more data in my field, it started to slow things down, so that even my rather pedestrian typing became sluggish. Each check was first removing any existing markers (text style & colour) and then searching for new errors and applying the markers again. This is quite a lot of work for a field object. I suppose I could have tried to make the spell checking more efficient and only checked the currently selected word, or words close to it, but cutting & pasting could have caused havoc, so I preferred to check the complete field at once.
My solution was to queue a spell check so that it waited until there was a delay in the typing and then checked all at once.
Here is the script that I placed in the field that I wanted to check:
local sLastKeystroke
on rawKeyUp pKey
-- only spell check if there have been no keystokes for 1000 millisecs
if sLastKeystroke is empty or sLastKeystroke is not a number then put 0 into sLastKeystroke
if the milliseconds - sLastKeystroke > 1000 then
-- only check spellingafter space, tab, delete, backspace, return, enter, full stop, comma
-- use KeyCoder to get these numbers
put "32,65289,65293,65421,65288,65535,46,44" into tSpellKeys
if pKey is among the items of tSpellKeys then
updateSpelling
end if
else
-- still typing, queue a spell check & try again in half a second
-- this check will be cancelled and a new check scheduled if I keep typing fast
cancelMessageName "updateSpelling"
send "updateSpelling" to me in 500 millisecs
end if
put the milliseconds into sLastKeystroke
pass rawKeyUp
end rawKeyUp
on cancelMessageName pHandlerName
put the pendingmessages into pMess
repeat for each line p in pMess
if item 3 of p contains pHandlerName then cancel item 1 of p
end repeat
end cancelMessageName
It uses a script local variable to store the time in milliseconds of the last key stroke. If you have stopped typing for at least one second, then press space, spelling will be checked immediately. However if you keep typing, then a "send in time" will be used to tell the system to check spelling in 500 milliseconds. Every time you press another key, this scheduled check gets pushed further into the future, but when you finally stop typing for more than half a second, the spell checker gets it's turn.
The timings of this might need to be adjusted for a more expert typist than me, but this suits me very well.
So now we have the "updateSpelling" handler being triggered appropriately, I guess it's time to work out what it should do.
The first thing is to clear any existing spell checker marks.
on clearSpellingMarkers pFieldName
lock screen
repeat with c = 1 to the number of chars in the text of pFieldName
put the textstyle of char c of pFieldName into tStyle
if tStyle contains "underline" then
replace "underline" with "" in tStyle
if char 1 of tStyle = comma then delete char 1 of tStyle
if char -1 of tStyle = comma then delete char -1 of tStyle
set the textstyle of char c of pFieldName to tStyle
end if
end repeat
set the textcolor of char 1 to -1 of pFieldName to ""
end clearSpellingMarkers
This handlers get sent the name of the field to be cleared and then removes any underlines and sets the textColor back to the default. This means that ALL colours will be removed, not just those that indicate incorrect spelling. While this does not worry me, if it is a problem, the spell checking can be changed so it uses a text style notification only, and doesn't change any colours. Text styles other than underlines will be preserved.
Now I can use one of the functions from last time to find the words in the field that need to be marked up. And this handler puts it all together and displays the incorrect words.
on showBadWords pFieldName
lock screen
put the text of pFieldName into tText
put listBadWords(tText) into tBadCharNumbers
if tBadCharNumbers is empty then exit to top -- no incorrect spellings
repeat for each line L in tBadCharNumbers
put item 1 of L into tStartChar
put item 2 of L into tEndChar
if tStartChar is not a number or tEndChar is not a number then next repeat
-- don't mark single characters
if tEndChar - tStartChar < 2 then next repeat
put the textstyle of char tStartChar to tEndChar of pFieldName into tOld
if tOld is empty then
put "underline" into tNew
else
put tOld & ",underline" into tNew
end if
set the textstyle of char tStartChar to tEndChar of pFieldName to tNew
set the textcolor of char tStartChar to tEndChar of pFieldName to "red"
end repeat
end showBadWords
This uses the listBadWords() function from the last post and then marks up the incorrect words as discussed.
Now the demo stack from last time has been altered. Instead of having a separate field to show the incorrect words, it just has a single field that will mark up mistakes as you type.
The handlers are all in the field or in the stack script.
All that's left is to work out how to correct spelling errors and how to apply this to a stack with multiple edit fields, without duplicating the scripts in each field.
Until next time,
Sarah