Invoke a Dialog Callback Procedure From Within a Dialog Callback Procedure?

Started by USMuscle, August 09, 2016, 11:55:02 AM

Previous topic - Next topic

USMuscle

I wrote 3 dialog callback procedures, all of which work as designed when run by themselves. I would like to run two of them as "subroutines", for lack of a better term, from within a "main" dialog. I've discovered you can't put a callback procedure inside another one, and that you also can't make a GoSub for the auxiliary callback procedures because the main callback procedure can't "see" anything outside of its function. Is there some other way I can accomplish this? If it helps, the reason I need this is:

The main dialog contains a mix of editboxes people need to fill out and and itemboxes from which to make selections. I want the people to be able to open other dialog boxes to perform functions related to the main dialog box. I am aware that you can't pass variables between functions, but I have devised an alternative method for that which works just fine. So, I just need a way to have the main dialog be able to open the two other dialogs, both of which also have callback procedures in them.

td

All you need to do is change your main dialog callback to a '#DefineSubroutine' instead of a '#DefineFunction' so it will have caller scope.  This will allow the 'Dialog' function for your child dialog template see the template variables.  Or you can place the child dialog templates directly in the user-defined-function so that the temple variables have function scope.   

Any user-defined-functions(UDF) and user-defined-subroutines(UDS) can be called by any other UDF or UDS as long as it has been defined before it is called and no UDF or UDS can be defined inside another UDF or UDS.
"No one who sees a peregrine falcon fly can ever forget the beauty and thrill of that flight."
  - Dr. Tom Cade

USMuscle

Thank you VERY much! Not sure if I used the syntax you had in mind for "calling" the child subroutine, but I basically defined both Subroutines, then "nested" the Child Dialog statements within the Parent Subroutine, kind of like this:

#DefineSubroutine Child1.........
....................
....................
#EndSubroutine

#DefineSubroutine Parent.........
....................
...................
     Case [Control Number for Button]
     [Child Dialog lines]
     [Child Dialog lines]

#EndSubroutine

Parent Dialog
[Parent Dialog lines]
[Parent Dialog Lines]

Sorry so cryptic, but basically the Parent Dialog displays when I run the script, and when I click the Button in the Parent Dialog, the Child Dialog pops up and everything works as expected. If you meant for me to use a different (better) syntax, I'm all ears.

One other benefit I noticed is that by making the Parent and Child both Subroutines instead of Functions, I can actually pass variables between the two Subroutines, which begs the question: For what purpose might I choose to make Functions instead of Subroutines, when Subroutines seem to offer more flexibility?

Thanks again, so, SO much! :)

td

You needed to either use a subroutine or define the child template within the parent callback but you didn't need to to both.  Although, obviously,  you can do both.

Subroutines and functions both have their uses and benefits.  Functions have benefits relating to the software design principles of  maintainability, data encapsulation and reusability among other things.
"No one who sees a peregrine falcon fly can ever forget the beauty and thrill of that flight."
  - Dr. Tom Cade

USMuscle

So are you saying I could have placed the Child dialog lines in a Label OUTSIDE of the Parent Subroutine, below the Parent dialog lines, and simply used a GoSub from within the Parent Subroutine? If that's the case, could I then place the defined Child Subroutine below the Parent dialog lines, directly above the Child dialog lines, or must I leave the defined Child Subroutine ABOVE the Parent Subroutine?

td

Quote from: USMuscle on August 11, 2016, 08:54:04 AM
So are you saying I could have placed the Child dialog lines in a Label OUTSIDE of the Parent Subroutine, below the Parent dialog lines, and simply used a GoSub from within the Parent Subroutine?
No, I did not say that.  It is not necessary to use a label and a 'gosub' when using a WIL dialog. However, you can, if you feel compelled to do so for some reason.  What I said was that the a dialog's templates variables must be instantiated and in scope before you call the Dialog function to display the dialog associated with that template.  UDFs and UDS's are globally visible once defined so you simply need to make sure that any callback UDF or UDS is defined before you call the Dialog function that displays the dialog that uses the UDF or UDS as a callback.
Quote
If that's the case, could I then place the defined Child Subroutine below the Parent dialog lines, directly above the Child dialog lines, or must I leave the defined Child Subroutine ABOVE the Parent Subroutine?

See above.  Or wait for someone with more patience to do a better job of explaining it.
"No one who sees a peregrine falcon fly can ever forget the beauty and thrill of that flight."
  - Dr. Tom Cade

USMuscle

I guess my problem is that I'm not sure how else I can "call" the Child dialog from within the Parent UDS without placing the dialog element lines inside the Parent UDS. That's why I was wondering about the GoSub.

As I tried to depict in my previous example structure, I want someone to be able to press a button in the Parent dialog that brings up the Child dialog. I didn't know how else to get the Child dialog to display without placing the Child dialog lines themselves within the Parent UDS. It is definitely unwieldly looking at a structure like what I have now, and since you said I didn't have to do that, can you offer some guidance as to an alternative I might use to call up the Child dialog instead of me having the Child dialog lines in the Parent UDS?

Thanks again.

td

Quote from: USMuscle on August 11, 2016, 12:30:34 PM
I guess my problem is that I'm not sure how else I can "call" the Child dialog from within the Parent UDS without placing the dialog element lines inside the Parent UDS. That's why I was wondering about the GoSub.

It's very simple.  Just use the cut and paste functionality of your editor of choice to move the 'child' template out of the 'parent' UDS and into the main body of your script.  Just remember to put it someplace before the 'parent' dialog's Dialog function gets called.

Quote
As I tried to depict in my previous example structure, I want someone to be able to press a button in the Parent dialog that brings up the Child dialog. I didn't know how else to get the Child dialog to display without placing the Child dialog lines themselves within the Parent UDS. It is definitely unwieldly looking at a structure like what I have now, and since you said I didn't have to do that, can you offer some guidance as to an alternative I might use to call up the Child dialog instead of me having the Child dialog lines in the Parent UDS?

Thanks again.

What you are trying to do has been understood from the beginning and all of the responses have been a modest attempt to provide guidance toward that end...  Perhaps you need a better 'explainer'.
"No one who sees a peregrine falcon fly can ever forget the beauty and thrill of that flight."
  - Dr. Tom Cade

JTaylor

Hopefully I am understanding the problem/question....Perhaps the following will help.   It is one option and I *think* what Tony was trying to get across. 

What I have done is write a processing script which integrates Dialog code into my script when I am ready to compile it.   I just use a Call() on the dialog file while developing and don't really care how ugly the code is at compile time.  This avoids a lot of hassle with editing dialogs and having to copy/paste them into my scripts.

Jim

#DefineSubroutine Child1_Dialog.........
....................
....................
#EndSubroutine


#DefineSubroutine Child1_Callback.........
....................
....................
#EndSubroutine

#DefineSubroutine Parent_Callback.........
....................
...................
     Case [Control Number for Button]
        Child1_Dialog()

#EndSubroutine

Parent Dialog
[Parent Dialog lines]
[Parent Dialog Lines]

JTaylor

Also, MAKE SURE you do not use the same dialog variable name for both dialogs....that doesn't work very well.

Jim

USMuscle

Quote from: td on August 11, 2016, 01:45:52 PM
It's very simple.  Just use the cut and paste functionality of your editor of choice to move the 'child' template out of the 'parent' UDS and into the main body of your script.  Just remember to put it someplace before the 'parent' dialog's Dialog function gets called.

Below are my Child dialog lines. The Child UDS is defined near the top of my script, above the Parent UDS. If I take the "Dialog lines" below out of the Parent UDS, and place them BEFORE the Parent UDS, the script displays the Child dialog first, rather than the Parent dialog.

SelectFormat=`WWWDLGED,6.1`
   
SelectCaption=`Select Item`
SelectX=9999
SelectY=9999
SelectWidth=186
SelectHeight=251
SelectNumControls=012
SelectProcedure=`SelectCallbackProc`
SelectFont=`DEFAULT`
SelectTextColor=`DEFAULT`
SelectBackground=`DEFAULT,231|230|222`
SelectConfig=0
               
Select001=`143,233,036,012,PUSHBUTTON,DEFAULT,"&OK",1,5,32,DEFAULT,DEFAULT,"231|230|222"`
Select002=`101,233,036,012,PUSHBUTTON,DEFAULT,"&Cancel",2,6,DEFAULT,DEFAULT,DEFAULT,"231|230|222"`
Select003=`003,005,170,008,STATICTEXT,DEFAULT,"Enter contact, or select a contact from the Address Book below.",DEFAULT,3,DEFAULT,DEFAULT,DEFAULT,DEFAULT`
Select004=`003,047,134,010,EDITBOX,NewAddress,DEFAULT,DEFAULT,3,DEFAULT,DEFAULT,DEFAULT,DEFAULT`
Select005=`003,027,066,010,EDITBOX,FirstName,DEFAULT,DEFAULT,1,@csCurLeft,DEFAULT,DEFAULT,DEFAULT`
Select006=`005,019,030,008,STATICTEXT,DEFAULT,"First Name",DEFAULT,3,DEFAULT,DEFAULT,DEFAULT,DEFAULT`
Select007=`073,019,030,008,STATICTEXT,DEFAULT,"Last Name",DEFAULT,3,DEFAULT,DEFAULT,DEFAULT,DEFAULT`
Select008=`071,027,066,010,EDITBOX,LastName,DEFAULT,DEFAULT,2,DEFAULT,DEFAULT,DEFAULT,DEFAULT`
Select009=`005,039,046,008,STATICTEXT,DEFAULT,"Address",DEFAULT,3,DEFAULT,DEFAULT,DEFAULT,DEFAULT`
Select010=`003,071,174,156,ITEMBOX,AddressBook,DEFAULT,DEFAULT,4,DEFAULT,DEFAULT,DEFAULT,DEFAULT`
Select011=`004,063,170,008,STATICTEXT,DEFAULT,"(Double-click an entry to remove it from the Address Book)",DEFAULT,3,DEFAULT,DEFAULT,DEFAULT,DEFAULT`
Select012=`003,231,070,016,VARYTEXT,localdir,"localdir for callback procedure function only",DEFAULT,16,1,DEFAULT,DEFAULT,DEFAULT`
               
ButtonPushed=Dialog("Select")

USMuscle

Quote from: JTaylor on August 11, 2016, 02:05:09 PM
Hopefully I am understanding the problem/question....Perhaps the following will help.   It is one option and I *think* what Tony was trying to get across. 

What I have done is write a processing script which integrates Dialog code into my script when I am ready to compile it.   I just use a Call() on the dialog file while developing and don't really care how ugly the code is at compile time.  This avoids a lot of hassle with editing dialogs and having to copy/paste them into my scripts.

Jim

#DefineSubroutine Child1_Dialog.........
....................
....................
#EndSubroutine


#DefineSubroutine Child1_Callback.........
....................
....................
#EndSubroutine

#DefineSubroutine Parent_Callback.........
....................
...................
     Case [Control Number for Button]
        Child1_Dialog()

#EndSubroutine

Parent Dialog
[Parent Dialog lines]
[Parent Dialog Lines]

Thanks for contributing here, JTaylor. A few questions, if you don't mind.

1) Is "Child1_Dialog()" an undocumented command, or something (yes, I understand the "Child 1" portion is dependent upon dialog name)? I can't seem to find such a syntax in the Help.

2) If that is indeed some kind of command to call a dialog (I assume it's a single-line command like a GoSub or GoTo), where should the dialog lines be placed in the structure?

3) Or, is the "Child1_Dialog()" your way of notating that that's where all my dialog code (without the related UDS stuff) goes? If that's the case, that's exactly what I have now.

Thanks for any clarification.

JTaylor

#3....Then I guess I don't understand the problem.   If you have a the child dialog in a SubRoutine then you should be able to call it when you click the button on the Main dialog.   It should then display and call the Child_Callback.  I do this all the time.   What happens when you click the button if you have things as I have outlined them.


Jim

USMuscle

Quote from: JTaylor on August 11, 2016, 03:48:43 PM
#3....Then I guess I don't understand the problem.   If you have a the child dialog in a SubRoutine then you should be able to call it when you click the button on the Main dialog.   It should then display and call the Child_Callback.  I do this all the time.   What happens when you click the button if you have things as I have outlined them.


Jim
It works just like you describe, and as intended. I kind of suspected that's what you meant when you said you didn't care how ugly the code looked that way.  ;) I just thought TD was saying that I did NOT have to put the Dialog box code in the Parent Subroutine, and that I could just move it into the main body of the script, and use some other command/statement to call the Dialog box itself. I guess TD meant that I should move the Child UDS out of the Parent UDS, which I had previously already done.

I guess this was one of those things wherein you know what you're trying to convey when writing something, but once written, the conveyance does not come out quite like you thought, and your communication gets misinterpreted.  :P

JTaylor

Okay.  So all is good?

To clarify what I meant...my code during development would look something like the following.  My processing script comments out the Call() function line and inserts the actual dialog code at that point.  I never see the code after that point (unless I have some weird problem I can't figure out) and is why I don't care if it is ugly or not.   If I inserted my dialog code in my development code I would probably do something like suggested here as I am a bit obsessive-compulsive about my code being formatted all the same.

Jim


#DefineSubroutine Child1_Callback.........
....................
....................
#EndSubroutine

#DefineSubroutine Parent_Callback.........
....................
...................
     Case [Control Number for Button]
        Call("child_dialog.wdl","")

#EndSubroutine

Call("main_dialog.wdl","")

USMuscle

Quote from: JTaylor on August 11, 2016, 04:35:18 PM
Okay.  So all is good?

To clarify what I meant...my code during development would look something like the following.  My processing script comments out the Call() function line and inserts the actual dialog code at that point.  I never see the code after that point (unless I have some weird problem I can't figure out) and is why I don't care if it is ugly or not.   If I inserted my dialog code in my development code I would probably do something like suggested here as I am a bit obsessive-compulsive about my code being formatted all the same.

Jim


#DefineSubroutine Child1_Callback.........
....................
....................
#EndSubroutine

#DefineSubroutine Parent_Callback.........
....................
...................
     Case [Control Number for Button]
        Call("child_dialog.wdl","")

#EndSubroutine

Call("main_dialog.wdl","")

OK, this is something like what I thought TD was alluding to (so the Main UDS doesn't get cluttered up with other Child Dialog code), except instead of having the Dialog code in a separate file, it would be somewhere in the main body of the script.

I like the modularity of your development-mode setup. Makes things a lot less unwieldly that way.

Once you're done with development, do you then replace "Call("child_dialog.wdl","")" with the actual Child dialog code, or do you actually distribute the associated .wdl file with the script?

JTaylor

My processing script does  a batch change and replaces all the Call()s with the associated dialog code.   You can't distribute the wdl files in their text form.   They could be compiled as wbc files but that is just a lot of extra files with which one would have to deal.   For example, one of my applications has around 100 dialogs so trying to maintain/modify those within the development code or compiling/distributing all of them separately would be a bit of hassle.

Jim

USMuscle

I agree with not having to distribute extraneous files, as well, and I like the idea of your script "processor" upon final compilation. I can see how useful that would be, as I am a big fan of modularization. Would you mind if I tinkered with making one of my own, or would that infringe upon your design copyright?  ;D

JTaylor

No problem with me.   For what it is worth, this makes a number of assumptions but should get you started.   

Jim

Code (winbatch) Select

  Home_Path = DirScript()
  DirChange(Home_Path)

; If !DirExist("compile") Then DirMake("compile")

  file_list = FileItemize("*.wbt")
  icnt = ItemCount(file_list,@TAB)

For x = 1 to icnt

  wbt_file = ItemExtract(x,file_list,@TAB)
  new_file = StrCat("compile\",wbt_file)
  wbt_cnt = 0
  wbt_text  = ""
  If new_file == "compile\compile.wbt" Then Continue

  BoxText(wbt_file)
  or_wbt=FileOpen(wbt_file,"READ")
  ow_wbt=FileOpen(new_file,"WRITE")
  line_read = ""

  While line_read != "*EOF*"
    dlg_text = ""

    line_read = FileRead(or_wbt)
    If line_read != "*EOF*" Then

      If StrIndex(line_read,"Call(",0,@FWDSCAN) > 0 && StrIndex(line_read,"DllCall(",0,@FWDSCAN) == 0 Then
        line_read = StrCat(";",line_read)                        ; Comments out the Dialog Call
        file_name = ItemExtract(2,line_read,"(")
        file_name = ItemExtract(1,file_name,",")
        file_name = StrReplace(file_name," ","")
        file_name = StrReplace(file_name,'"',"")

        If FileExist(file_name) Then dlg_text = FileGet(file_name,"")

      EndIf
      wbt_text = wbt_text:line_read:@CRLF
      If dlg_text != "" Then wbt_text = wbt_text:@CRLF:dlg_text:@CRLF
    EndIf
  EndWhile

  FileWrite(ow_wbt,wbt_text)
  FileClose(or_wbt)
  FileClose(ow_wbt)
Next




USMuscle

Wow, thanks for the great head start! And, for clearing things up on the Child dialog stuff.  ;)

td

Quote from: JTaylor on August 11, 2016, 08:57:26 PM
No problem with me.   For what it is worth, this makes a number of assumptions but should get you started.   

Jim

Is there some reason why you don't simply use #include statements to embed one one script in another?  Included scripts do not need to be compiled separately nor do they need to be shipped as an additional file.  The compiler automagically expands the included script in the including script before compilation begins.  The only restriction I am aware of is that iyou can't nest include more than 16 deep.

And thanks for assisting on this topic.   Hopefully, the OP understands that you can place a 'child' dialog template in the main body of a script and you don't need to use a Call statement either.  All that is required is that  'parent' dialog's callback be a subroutine instead of a function.
"No one who sees a peregrine falcon fly can ever forget the beauty and thrill of that flight."
  - Dr. Tom Cade

JTaylor

I use #includes but not for the dialogs.  I want to be able to edit the dialogs in the Dialog Editor and I assume if I "include" them they would have to be bound in UDFs/UDSs so they don't launch inappropriately and so that I have some way to launch the one I want when I want it.   Is this not correct?

I think the confusion is around the words "main body" of the script.   I believe the OP is interpreting that to mean outside any of the UDFs/UDSs but as they noted it launches inappropriately if one does that.   I assume you mean within the Parent Callback routine within the Pushbutton section?    If not, then maybe I have missed something over the years as this would tie in with the above as it "could" seem to mean one could just plop in dialog code anywhere without any bounds and somehow call it....but this doesn't seem like a reasonable interpretation.


Jim

td

Quote from: JTaylor on August 12, 2016, 07:54:30 AM
I use #includes but not for the dialogs.  I want to be able to edit the dialogs in the Dialog Editor and I assume if I "include" them they would have to be bound in UDFs/UDSs so they don't launch inappropriately and so that I have some way to launch the one I want when I want it.   Is this not correct?

I am not sure I completely understand what you are saying.  Using an include does not require that the dialog be bound to a UDF or UDS.  The contents of an included file are simply expanded into the including file at the point of the '#include' statement.   When you place a dialog template in an include, you simple select  the "Create a dialog function that WinBatch will ignore" option on the Configuration property page.   This allows you to edit your template in the dialog editor form the include file but the Dialog statement in the include file will not launch the dialog from the including file.  You place the 'real' Dialog function in the including file as you see fit.

Quote
I think the confusion is around the words "main body" of the script.   I believe the OP is interpreting that to mean outside any of the UDFs/UDSs but as they noted it launches inappropriately if one does that.   I assume you mean within the Parent Callback routine within the Pushbutton section?    If not, then maybe I have missed something over the years as this would tie in with the above as it "could" seem to mean one could just plop in dialog code anywhere without any bounds and somehow call it....but this doesn't seem like a reasonable interpretation.

If the OP is interpreting "main body" to mean outside any of the UDFs/UDSs then he is absolutely correct!  The dialog will not launch inappropriately, if it is outside of a UDS.   Perhaps a few things need to be explained.

The term dialog template refers to the set of variables that contain a dialog's description.  They are WIL variables and obey the same scoping rules as any other WIL variable.   So if you call a Dialog function from a UDS, the function can 'see' the template variables already defined outside the UDS and the dialog displays normally.  This is because UDS's have caller scope.  Which means that UDS's can 'see' all the existing  caller variables - including the template  variables.  Of course, UDFs are different because they do not have caller scope.

You can use the same "Create a dialog function that WinBatch will ignore" option to place a 'dummy' Dialog function call in the main body of the script so you can edit the template using the Dialog Editor.    You can then place the 'real' Dialog function that Displays the dialog in a 'parent' dialog's UDS callback.
"No one who sees a peregrine falcon fly can ever forget the beauty and thrill of that flight."
  - Dr. Tom Cade

JTaylor

Hmmmmmmmmmmmm....I think this is going to be one of those things that has never dawned on me over the last 20 years and have never seen anyone explain.  If I have the following, you are saying I can have that anywhere if I tell it to ignore it and then when I want to dialog to activate I can simply issue this?

    ButtonPushed=Dialog("MyDialog")

If so, wish I had figured that out years ago.   I just assumed all this time it all had to go together and the dialog would activate where it was encountered.   I think this will also clear things up in the previous discussion as well.

Jim


Code (winbatch) Select


MyDialogFormat=`WWWDLGED,6.1`

MyDialogCaption=`WIL Dialog 1`
MyDialogX=002
MyDialogY=038
MyDialogWidth=096
MyDialogHeight=064
MyDialogNumControls=002
MyDialogProcedure=`DEFAULT`
MyDialogFont=`DEFAULT`
MyDialogTextColor=`DEFAULT`
MyDialogBackground=`DEFAULT,DEFAULT`
MyDialogConfig=0

MyDialog001=`023,015,048,020,PUSHBUTTON,DEFAULT,".",1,7,@csDisabled,"Times New Roman|5632|40|18","192|192|192",DEFAULT`
MyDialog002=`027,019,040,012,STATICTEXT,DEFAULT,"Hello",DEFAULT,12,@csCenter,"Microsoft Sans Serif|8192|170|34","0|0|0",DEFAULT`

ButtonPushed=Dialog("MyDialog")








Jim

td

Create a dialog in the WIL Dialog Editor.  Display your dialogs attributes property page and select the 'Configuration' tab.  Once the tab is displayed, select the 'Creaet a dialog function that WinBath will ignore' radio button and press the OK button.  Now select the 'Script View' View menu item.   You will see the following in the last line of the displayed script:

Code (winbatch) Select
ButtonPushed=Dialog("MyDialog",0) ; WinBatch will not display this dialog.


Note the 0 second (optional) parameter.  The help file explains it better than I can:

"(i) ignore-flag: [optional] 1 to display dialog, or 0 to skip. Note: If not specified one (1) is assumed."

I think the documentation fails to mention why you would want to do this, e.g., because Dialog Editor will still load the dialog.

Please understand that you would put a 'Dialog("MyDailog")' line at the point in your script were you wish to display the dialog. 


"No one who sees a peregrine falcon fly can ever forget the beauty and thrill of that flight."
  - Dr. Tom Cade

td

Included file 'Dlgtemp.wbt"

Code (winbatch) Select
MyDialogFormat=`WWWDLGED,6.2`

MyDialogCaption=`WIL Dialog 1`
MyDialogX=906
MyDialogY=091
MyDialogWidth=313
MyDialogHeight=223
MyDialogNumControls=002
MyDialogProcedure=`DEFAULT`
MyDialogFont=`DEFAULT`
MyDialogTextColor=`DEFAULT`
MyDialogBackground=`DEFAULT,DEFAULT`
MyDialogConfig=2

MyDialog001=`082,207,034,011,PUSHBUTTON,"PushButton_OK",DEFAULT,"OK",1,10,@csDefButton,DEFAULT,DEFAULT,DEFAULT`
MyDialog002=`198,207,033,011,PUSHBUTTON,"PushButton_Cancel",DEFAULT,"Cancel",0,20,DEFAULT,DEFAULT,DEFAULT,DEFAULT`

ButtonPushed=Dialog("MyDialog",0) ; WinBatch will not display this dialog.


Including script:

Code (winbatch) Select
#include Dlgtemp.wbt

#DefineSubroutine Useless()

   return Dialog("MyDialog")
#EndSubroutine

; Display an included dialog
nReturn = Useless()



"No one who sees a peregrine falcon fly can ever forget the beauty and thrill of that flight."
  - Dr. Tom Cade

JTaylor

Thank you for pointing out something that should have been obvious.   One of those things where one starts out doing something one way and assumes that is how it has to be done. 

Jim

JTaylor

Just to make sure I am not missing something...wouldn't this do the same thing or is something gained with the SubRoutine?

Jim

Code (winbatch) Select

Code: Winbatch
#include Dlgtemp.wbt

; Display an included dialog
nReturn = Dialog("MyDialog")


td

Yes, it would be the same.   I just use a subroutine to connect the example to the rest of the topic.
"No one who sees a peregrine falcon fly can ever forget the beauty and thrill of that flight."
  - Dr. Tom Cade

JTaylor

Thought so but since I have been oblivious for 20 years I thought I should ask  :)

Jim