When we left off
last time, we had finished up with creating a brand new file, grabbing data for it, formatting the data into a line of tab-delimited text, and then writing that to the file.
Now, we're going to append data to an existing file, but first, I made some changes to the creating a new file code. Since this is tab-delimited text, it's reasonable to assume that it's going to get used in a spreadsheet application of some kind. Well, you'd be assuming, not me, since that's why I wrote it that way. However, I realized that header rows don't suck, so I created one of those in the new file, so that when you open it in something like Numbers or Excel, you get a nice header row that explains what each row does. I'm not going to put up the entire function again, just the new lines:
set theTempString to "Time" & " " & "SSID" & " " & "Authmode" & " " & "Channel" &
" " & "Data Rate (Mbps)" & " " & "Signal Strength (dbm)" & " " & "Signal Noise (dbm)" & " " & "MAC of WAP" & "
"
--createt the header line for the file
set theFileString to my NSString's stringWithString_(theTempString)
--AppleScript string to NSString
set theFileData to theFileString's dataUsingEncoding_(NSUTF8StringEncoding of current application)
--NSString to NSData
theFileHandle's writeData_(theFileData)
--Write the header to the new file
set theFileLengthPointer to theFileHandle's seekToEndOfFile()
--move the file pointer to the end of this string
--since we KNOW what created this line, we don't have to be paranoid, like we are
--with appending data
The first few lines are nothing major. We create a string with the column headers ended by a return, and shove that into an NSString. (We could save a line of code and just put the raw string in the stringWithString_() method, but this way is neater to read.
The new stuff is the set theFileLengthPointer to theFileHandle's seekToEndOfFile() line. What this does is move the "start writing data here" pointer to the end of the file, so that the next time we write data to that file, it's going to start in the right place. We use this in the appending function too:
on appendToExistingFile_(sender)
set theOpenPanel to my NSOpenPanel's openPanel()
--create an open panel
theOpenPanel's setCanChooseFiles_(1)
--the user can choose files
theOpenPanel's setResolvesAliases_(1)
--it will resolve aliases to their original file
theOpenPanel's setCanChooseDirectories_(0)
--the user cannot chose a directory, it makes no sense in this application's context
theOpenPanel's setAllowsMultipleSelection_(0)
--the user cannot choose multiple files, again, not appropriate for the application's context
set theOpenPanelResult to theOpenPanel's runModal()
--run the Open panel
if theOpenPanelResult is 1 then --if the user clicked okay
my appendToExistingDataButton's setEnabled_(false)
--disable the button to append to an existing data file
--we only work on one file at a time.
my createNewDataButton's setEnabled_(false)
--disable the button to create a new data file
--we're appending to a file, letting the user create a new one
--while this is going on would be dumb
set theDataFilePath to first item of theOpenPanel's filenames()
--get the file paths and names, in case we need them
--this returns an array with a single item, so we use that
set theDataFileURL to the first item of theOpenPanel's URLs()
--get the URL to the file, as that's what we should be using
--since this returns an array/list, we have to get the first item of that list
set theFileHandle to my NSFileHandle's fileHandleForWritingToURL_error_(theDataFileURL, missing value)
--create the file handle
set theFileLengthPointer to theFileHandle's seekToEndOfFile()
--set the file handle pointer to the current end of the file
set theFileString to my NSString's stringWithString_("
")
--we're going to write a single blank line just to be sure we aren't jamming on the end of the data.
--We don't know for sure what created this original file, so better an extra blank line than mangled data.
set theFileData to theFileString's dataUsingEncoding_(NSUTF8StringEncoding of current application)
theFileHandle's writeData_(theFileData)
set theFileLengthPointer to theFileHandle's seekToEndOfFile()
--seek to new end of file
set createDataFile to false
--make sure this flag is false
set appendToExisting to true
--we need this to be true
end if
end appendToExistingFile_
This code really works like the new file code with a few exceptions. Obviously we're not using the save panel, since the file already exists, so for this, we use NSOpenPanel instead. We set a few options, like allowing the user to choose files, resolving aliases if one is chosen, and disabling multiple files, and choosing directories, as neither of those make sense in this case. Once that's done, we use the open panel's runModal() to display the panel. If the user picks a file and hits okay, then we disable append and create new file buttons in the App UI, since we don't want another file being used while we're writing to this one.
Like we did with the save panel, we grab both the path and the URL, although unlike the new file code, we don't actually need the path. Also, unlike the save panel, NSOpenPanel returns us an array, or AppleScript list, not a text string. Since we need the text string, we set the file path and url variables to the first item of the array (Since we only allow the user to pick a single item, this assumption is fairly safe.):
set theDataFilePath to first item of theOpenPanel's filenames()
set theDataFileURL to the first item of theOpenPanel's URLs()
Next, we get the file handle for the file at the end of the URL via fileHandleForWritingToURL(), which gives us a file handle for the file we're going to be appending data to. Since we aren't dealing with error handling, we just pass nil or missing value to the method for the error parameter.
Now, we're appending data to the end of an existing file, so we want to make sure we're starting at the end of the file. Like we did in the new code for the new file code, we use seekToEndOfFile() to make sure we're starting at the end of the file. (Yes, as someone used to being far more explicit with such things in AppleScript, the 'trust me, the pointer's in the right place' thing going on here was a little disconcerting, but it obviously works, so...) Next, just because I want to be REALLY SURE that we can clearly tell where the data we're appending starts, (I'm paranoid, sue me), we write a single return to the file, then call seekToEndOfFile() again. So now we're at the end of a file, and we should have a blank line between the new and the old data.
We also make sure the createDataFile flag is false, and the appendToExisting flag is true. Once that's taken care of, we're done with this function, and we move on to the last of the new code for appending files in the loadData() handler.
This code is, as you can see, pretty much like the new file code:
if (appendToExisting is true) and (theSaveFileFlag is true) then
--both have to be true
set theTempString to (theTime's stringValue() as text) & " " & (currentSSID's stringValue() as text) &
" " & (authMode's stringValue() as text) & " " & (currentChannel's stringValue() as text) &
" " & (currentDataRate's stringValue() as text) & " " & (signalStrength's stringValue() as text) &
" " & (signalNoise's stringValue() as text) & " " & (currentWAPMAC's stringValue() as text) & "
"
set theFileString to my NSString's stringWithString_(theTempString)
--convert the AppleScript string to NSString
set theFileData to theFileString's dataUsingEncoding_(NSUTF8StringEncoding of current application)
--NSString to NSData
theFileHandle's writeData_(theFileData)
--write us some data
end if
The cleanup code when you're done writing is the same code as the new file code, so we don't have to go over it again.
The next update may be a while, I'm trying to dig up a way to do the signal vs. noise charts that will display directly in the application.