Popup Button won't show custom choice

I have 2 of these on my Invoice form, one for Terms (credit card, open account, etc.) and one for Shipping Method (FedEx Ground, UPS Ground, etc.). I have configured these with comma-separated lists in the Formula section, and have a short procedure specified to run when a choice is made. This is because inevitably there could be desired values for either or both of these that don’t match what’s in the list. So, the last item in each comma-separated list is a “?”, and if that is chosen the procedure prompts for the desired text, which is assigned to the field. All of this works fine, except when I enter a custom value it will not show up in the text box of the popup button object, it remains blank. The data sheet confirms the value I entered but it won’t show up on the form. Am I using the wrong method to do this?

Panorama uses Apple’s native code for user interface items like buttons, including pop-up menus. There is a discussion in the help about why this was done and what the ramifications are:

One ramification of this is that pop-up menu buttons will not show any value that is not listed as a choice for the menu. Apple’s code simply doesn’t allow this. So if you want a value to be displayed, it must be included in the list of choices. The only thing you can do is use a separate Text Display object to display the value. Or, you could modify the list of choices on the fly (but this could be tricky since different records could contain different exception values).

Is there an alternate method to handle this the way I need? In Pan 6, the list of values was entered into the Choices section of the field in the Design sheet, and of course it allowed me to do this. I’ve seen reference in this forum to “Choices” in Pan X but haven’t paid much attention. Is there a solution in there somewhere?

I’ll look into the separate Text Display object idea.

You could add code to the pop-up button procedure that will add the new choice to the formula for the pop-up menu. Here is code that would do that, assuming the button is one used to choose the shipping method:

 if fganswer="?"

     /*If the user chooses ?, then prompt for the new shipping method
     and assign that value to the field Answer*/

     local lvshipby,lvformula
     Gettext "Ship By: ",lvshipby
     Answer=lvshipby

     /*Add the new shipping method to the pop-up button formula
     and sort the list alphabetically; the pop-up button object is named Poppy*/

     lvformula=objectinfo("Formula","Poppy") 
     lvformula=arraysort(arraytrim(lvformula,1,",")+","+lvshipby,",")+",?"
     changeobject "Poppy", "Formula", lvformula

     /*Set the pop-up button data value to the newly entered ship method*/

     fganswer=lvshipby
     showvariables fganswer

else

     Answer=fganswer

endif

Now you have permanently added the new value to the list. If you don’t want to do that, you could reset the value of the pop-up button formula each time you open the form.

Tom, thanks for your effort on this. I’ve learned a number of things I wasn’t clear on. I can see some pitfalls using this method. This will eventually be a multi-user database, and one user adding to the formula in his own copy won’t propagate to everyone else’s, whose formulas don’t contain the new method. The modified formula could get pretty large. Or it we delete the new value from the formula after setting it, we’re back to not being able to display the value since it is an exception now.

But I came up with what looks like a workable method. I have a separate TextDisplay object for the same field, laid over the PopUpButton object, with the formula in this field testing whether the value in the field matches anything in the main formula (using arraysearch(). If there is a match, display blank (since the PopUp object will display the value), otherwise display the value in the TextDisplay object, since the PopUp shows blank. This is working well, but it brings up another issue I hope has an easy answer.

The formula for my ShipVIA field has (at the moment) 19 separate comma-separated values. I can foresee a maintenance nightmare having to update the literal arrays that constitute the formula for the PopUp, as well as the formula for the TextArray which has to match, if I decide to make a permanent change to the formulas. So I want to assign a fileglobal variable to this comma-separated array (in .Initialize), and just reference it in the formula for the PopUp, as well as in the arraysearch( function in my TextDisplay object, so I just have one maintenance point. If I just put the name of my array in the formula pane of the PopUp object, it just displays that name, not the choices. How do I force the Formula pane to interpret the variable I want to put there?

First in the Properties pane set the Popup to use a formula. If your variable is comma delimited you can use a formula like this:

replace(MyChoices,",",¶)

This replaces the commas with returns in your choices variable. If your variable is already return delimited you would just use the variable as the formula.

I see another problem with my approach in a multi-user database: any additions a user has made to the pop-up value will be overwritten whenever a new generation is downloaded assuming the PanX version works like the Pan6 version of new generations.
Your solutions sounds reasonable, and I have another one you could consider also. Just rebuild the formula for the pop-up button each time you open the form or add a new value to it.

 fganswer=arraysort(arraydeduplicate(arraybuild(cr(),"",{ShipVIA}),cr()),cr())+cr()+"?"

This would not require any maintenance. Just rebuild the formula value whenever needed. If there a lot of choices, you could get unwieldy length, but I wouldn’t have thought there are so many shipping options that it would become a problem.

Tom

Now you’re talking. I find the Mode settings for the PopUp Button Options confusing, it defaults to “comma separated array” so that’s what I constructed, but in Formula mode it wants a ¶ separated array. What you suggested works wonderfully; I have dodged another Apple induced programming bullet.

Tom, I’m not entirely following what you are suggesting, but it looks like I don’t need it. When you consider we ship by UPS and FedEx, and each one of them has many different “speeds” as well as international options, that list gets long if you want to be comprehensive. My concern is for the odd method that doesn’t fall into one of those categories; if the odd method becomes common I can just adjust the array in .Initialize to make it a standard choice.

Just to be clear, this is what I interpret as why this works. In Comma Separated Values mode, the literal values are entered into the Formula pane, separated by commas, and the commas are replaced with ¶ internally by Pan X to produce a workable list of options. In Formula mode, the Formula pane deals with its data interpretively, and the replace( function forces the ¶ separation, which it requires in any case. As you said, if I had created my variable as a ¶ separated array in the first place, no replace( would be necessary.

A further option might be to incorporate the listchoices( function in your formula. Assuming you have the basic choices in a return separated variable called fgShipChoices. You could use this formula for the popup with the threshold of the listchoices( set to 2. This means an entry must be found at least twice in the field before it is included - but this value could be set to whatever you determine is best.

arraydeduplicate(arraystrip(fgShipChoices+¶+listchoices(«ShipVIA»,¶,2),¶),¶)+¶+"?"

This takes your variable and adds a return plus the result of the listchoices( function but stripping the added return if the listchoices( is empty. Using arraydeduplicate( obviously removes any duplicate items of the combined arrays. Finally we add the “?” entry at the end of the list. Your fgShipChoices could not contain the “?” entry in this use. The down side of this is that the arraydeduplicate( function will also sort all the entries and they will be then listed alphabetically with the “?” entry at the end.

You’re welcome to use this non-sorting deduplicate if you wish Scott. I can’t recall why I used fileglobals - locals should work.

/*

removes duplicate elements from a text array without sorting the array (which the [ArrayDeDuplicate] statement does).

The array to be deduplicated.
The deduplicated array.
The array separator used in each of the arrays.

The statement removes duplicate elements from a text array without sorting the array (which the [ArrayDeDuplicate][] statement does). The expression, ArrayDeDupNoSort "w,e,w,e,rr,t,r,e,w,t,r", NewArray,"," will set NewArray equal to: "w,e,rr,t,r" */

; ***************************************************************************
; ArrayDeDupNoSort 20.11.2016
FileGlobal OldArray, NewArray, ArraySep
OldArray = parameter(1)
ArraySep = parameter(3)
; Deduplicate the array, leaving empty elements where duplicates have been removed, and
; strip out the empty elements.
NewArray = arraystrip(ArrayFilter(OldArray,ArraySep,
{?(arraysearch(arrayrange(OldArray,1,seq()-1,ArraySep),import(),1,ArraySep)>0,“”,import())}),
ArraySep)
; Return unsorted array via parameter
SetParameter 2,NewArray
Return

On further thought, I guess you could eliminate the sorting by not using arraydeduplicate( at all and instead use a variation with arraydifference(:

arraystrip(fgShipChoices+¶+arraydifference(listchoices(«ShipVIA»,¶,2),fgShipChoices,¶),¶)+¶+"?"

This takes the variable and combines it with an array made up of the difference between the listchoices( and that variable with the “?” tacked on the end. No sorting is involved so you have your intact variable list on top of the newer listchoices items.

That didn’t translate very well but the code is quite straightforward.