SPELUNKING WITH VIEWFRAME Rob Bruce CorNet RobBruce@aol.com **************************************************************************** This article is reprinted from issue 2.2 (March/April 1994) of PIE Developers magazine. Copyright(C) 1996 by Creative Digital Publishing Inc. All rights reserved. **************************************************************************** In my current application I need to provide the user with several bits of functionality which already exist in the Newton. Rather than increasing my package size and wasting my time recreating these features with all new code, I decided to use built-in code instead. For instance, in our application we want to allow the user to phone a client. This is a simple task if you use the built-in Names application. However, if you have to write your own code it might take several hours to reproduce the call slip and all of itÕs features. The callSlip function checks area codes against the current area code and modifies the telephone number accordingly. It also uses built-in system variables to store the telephone company access code and the userÕs calling card numbers. I wasnÕt interested in recreating all these capabilities. However, if I attempted to open callSlip using Getroot().callSlip: Open(), I would get an error -48200 ÐÊexpecting a frame or array. After looking over callSlip, I was worried that I was expected to pass it a Names soup entry. Since my record wasnÕt in the Names soup, I almost gave up. Fortunately, I remembered that you can write Òcall Jamie 555-1212Ó on the NotePad, tap Assist and the call slip comes up. With that in mind, I started my Newton and tried once again to open the call slip using Getroot().callSlip:Open(). When my Newton crashed, I looked at the package with ViewFrame to see what could be wrong. Several variables had been instantiated which werenÕt there before the package was launched. I assumed that one of them was the slot which callSlip expected to be set before it opened. Among the contenders was a slot called fields which seemed to have a lot of potential. I quickly wrote Òcall Jamie 555-1212Ó on the NotePad, tapped Assist, and then took another look with ViewFrame. With the call slip open, I looked at the frame which was set by the Assistant. It looked like this: {alternatives: nil,dialdirect:Ó555-1212", rawText:ÓJamieÓ,phone:Ó555-1212"} Because it uses the MacÕs keyboard for data entry, I entered my next experiment into the Inspector: getroot().callslip.fields:={ alternatives: nil, rawText:ÓJamieÓ, dialdirect:Ó717-555-1212"}; getroot().callslip:Open(); A couple of quick tests determined that dialdirect was the number which the Inspector generated and that the phone number was an internal slot created by callSlip. So much for that part of the problem. When I tap the Call button, up pops a request for me to add this person to the Names soup. I wasnÕt really interested in having everyone see this dialog. I went back to ViewFrame, into the callSlip._proto, looking for the viewChildren: GetRoot().callslip._proto.viewChildren RO Array:viewChildren #8 Ref(2835473) 0: {#2} callslip cancel button 1: {#7} text:Call 2: {#6} text:Options 3: {#11} 4: {#5} callFeedback 5: {#14} 6: {#7} 7: {#8} text:Add to Names? It looks like child #1 with the value text:Call is a good pick. It turns out to be a protoTextButton Ð a quick look at itÕs ButtonClickScript points me to a function called Speed-Dial. HereÕs what SpeedDial looks like with ViewFrame: GetRoot().callslip._proto.SpeedDial RO Frame:CodeBlock #5 Ref(2832353) func() begin local speedyCallback; IF NOT(userConfiguration.currentDialSpeaker = Ôspeaker) GOTO 33 SetValue(callFeedback, Ôtext, ÒWill start dialing in 3 secondsÓ); RefreshViews(); Sleep(3 * 60); 33: IF NOT(userConfiguration.useDialNavigator) GOTO 51 vars.navigator.dialNavigator GOTO 52 52: IF NOT(nil) GOTO 71 :DialNavigate(callPhoneLine.entryLine.text); GOTO 87 71: :Dial(callPhoneLine.entryLine.text, userConfiguration.currentDialSpeaker); 87: AddDeferredAction(AddCardHelper, []) end IÕm not interested in most of this code. However, the last line calls another function, AddCardHelper. As I ramble off to figure out what AddCardHelper does, I realize I donÕt really care Ð I just want it to disable it. In fact if I could have it close the call slip that would be great. So I add another line to my script to get: getroot().callslip.fields:={ alternatives: nil, rawText:ÓJamieÓ, dialdirect:Ó717-555-3333"}; getroot().callSlip:Open(); getroot().callSlip.AddCardHelper:= func() begin callSlip:close(); end; This last line overrides the built-in AddCardHelper slot with my script. It occurs to me that this is pretty messy. IÕve semi-permanently altered the function of this object by overriding the built-in slot. I need to remove my hack after IÕm done but the timing must be right. The final product looks like this. getroot().callslip.fields:={ alternatives: nil, rawText:ÓJamieÓ, dialdirect:Ó717-555-3333"}; getroot().callSlip:Open(); getroot().callSlip.AddCardHelper:= func() begin callSlip:close(); adddelayedAction(func() removeslot(getroot().callSlip,ÕAddCardHelper),[],200); end; Now the patch disappears shortly after the call slip is dialed. This short section of code saved me time and RAM, both valuable resources. My users donÕt have to worry about what ÒAdd to Names?Ó means, and all that was added to my program was a short section of code rather than the complete logic which was already built into the NewtonÕs ROM.