How To Articlesby Thom Parker of WindJack Solutions.
Copyright © 2004 by WindJack Solutions
Let's start off looking at the app.popUpMenu method. It's simpler and structurally both methods are identical, so everything we do with the easier to use method will apply to the other.
This method will take any number of arguments. Each argument is either a string or an array of strings. If we go with the simple case, strings only, we can create a single level list style menu. The menu items are the same as, and appear in the same order as the arguments to the popUpMenu function.
var result = app.popUpMenu("apple", "orange", "coconut", "mango");
To create a sub menu we'll replace one of the string arguments with an array of strings. The first element of the array is always the parent item and the following elements form the items of the submenu.
app.popUpMenu("apple", ["oranges", "navel", "valencia", "blood"], "coconut", "mango");
It is important to note that parent items cannot be selected from the menu and therefore cannot be returned by the popUpMenu method. In the example above, oranges is the only parent item in the menu and so it will never be returned. The sub menu items immediately below the parent item are called the child items, some of which may also be parent items as we'll see in the next example. Only menu items having no children can be returned from the popUpMenu method. These items are called leaves. This terminology is consistent with any hierarchical, or tree type structure.
Simple so far, but what if we want yet another level in the menu. Well, this is easy too. It's just an extension of what we have already done, i.e. replace any string, except the first element in an array, with an array of strings. Let's reorganize the menu from the previous example to try this out.
One last note on this topic. The first element of any array is always the parent element of a sub menu. If it is replaced with another array, or any other data type, the string representation of this value would still be the parent item. The results of such a replacement may be a funny looking and meaningless menu since none of the elements of the replacing item will contribute to the menu list.
Everything stated so far is also true for the newer popUpMenuEx method. Just replace the word string in the previous discussion with the word object, and that's practically, but not entirely, all you need to know. The new method was created to solve several problems with the first version. The biggest problem being that the return value is the name of the selected menu item. The construction of even moderately complex menus is restricted by this method of operation. For example, a menu constructed to control the hidden property of a form field might look like this.
If this menu was constructed with the popUpMenu method the return value is either hide or show. This value tells you the action being requested but not the name of the field to apply it to. To resolve this issue you'd need to include all the information in the name of the menu item. The menu item names can quickly become too long to be useful as the menu becomes more complex.
Other entries in the menu item object are the bMarked and bEnabled properties. These properties control the state of a menu item and convey more meaningful information to the user than the item name alone. They also allow you to create more compact menus. For example, to give the menu shown above more meaning, the bMarked property can be used to place a check mark next to either show or hide to indicate the current state of the field. Alternatively, to make the menu more compact, a check mark can be placed on a field item to indicate the visible state, thus removing the need for a submenu.
A big difference in how the two Popup Menu methods are used is in how the child items are specified. The menu item object has a property, oSubMenu, for specifying the child menu item objects, rather than simply tacking them onto an array as we did previously. The value of this property can be either a single menu item object or an array of them. Below is the code for the fields menu shown above using the popUpMenuEx method
var strResult =
The result from this menu is a bit more complicated than in the previous examples. If the user selects the menu item field1->hide the returned value is field1:hide. This value now needs to be parsed into two separate values, 1) the action to perform and 2) the name of the field the action is applied to. The code line below shows a simple method for accomplishing this parsing.
var arrayVals = strResult.split(":");
The result of this line is an array of strings split out of the variable strResult that was returned from the popUpMenuEx method, arrayVals contain the field name and arrayVals contain the action to be applied to the field.
Techniques for Using a Popup Menu:
In the examples shown so far all the menu items are static. That is, they are explicitly set up as constant strings, objects, and arrays in the code. This technique is fine for all those menus for which you already have defined entries ahead of time. But for those menus whose contents are not known at the time you write the code, the arguments to the popup menu method have to be built at run time.
The first problem we run into becomes apparent when we try to programmatically build the argument list in the first example. Let's say the list of fruits is stored in an array, but we don't know the size of the array until runtime. The app.popUpMenu method shown in the example has 4 arguments, but if we don't know this up front we can't hard code the method call with 4 arguments. Maybe the array will contain 3 or 5 items, we just don't know. One way to solve this problem is to rearrange the structure of the menu. We'll pass just one argument. As shown in the following code that one argument is an Array.
var arrayOfFruits = new Array("apple",
"orange", "coconut", "mango");
This style of menu can be quite useful when a menu header is necessary to provide some extra information to the user. A good use would be for a situation in which the user cannot be expected to know the exact purpose of the menu from it's context or entries alone. In this example the header shows it's a Fruits menu.
The apply method takes two arguments. The first is an object that will become the this object inside the function. The second argument is an array of values that will be used as the arguments to the function. So now we can call the popUpMenu function with an arbitrary number of arguments.
app.popUpMenu.apply( app, arrayOfFruits );
Notice the first
argument is app. The this object inside a function
represents the context the in which the function is called.
The context of popUpMenu function is app since it is a
member of app. The second argument is the array of
strings that are our top level menu items. The result
of this operation is a menu identical to the one in the
first example. Creating the menu this way is even easier
than in the previous technique where we used the menu header
A Useful Example:
Now let's put everything we've learned so far into creating a real world application. The easiest things to express in a menu are those things that are naturally a list or tree. Several items in a PDF fit this format: Fields, Bookmarks, Annotations, Links, Pages, Named Icons, Optional Content Groups, Templates, Security Handlers, Acrobat Toolbar Buttons and Menu Items. Literally any property or method that returns an array, or tree, of items can be easily expressed as a menu. An Object's properties and methods can also be displayed in a menu since an object can be treated as an Array.
For this example we'll create a menu for manipulating the properties of a form field on the current document. We'll use the popUpMenuEx method and both the bMarked and bEnabled properties to mark the menu item if the particular property is set and disable a menu item if the property is not valid for the field.
All of the work of creating a menu is in building up a list of items. Typically this is accomplished with a code loop for copying data from one list (the source Array) to another (the menu item Array). Submenus are created by nesting loops inside one another, one nested loop for each menu level. In this example there are two nested loops, so the resulting menu has two levels. The outer loop assembles the top level menu of Field names and the nested inner loop builds a submenu of properties for each field.
//** First we need a list
of names for the properties we are going to look at
The required and readonly properties of a Field Object apply to all instances of the Field on a document, but the hidden and print properties apply only to the specific Widget instances of a Field. So our script will work inconsistently for Fields with more than one instance. We can improve its' operation by adding another level of submenus that reference the widget instances of the field.
The instance information is stored in the page property of the Field. This property returns an Array if the field has more than one instance. To modify the existing code it is only necessary to add another nested loop using the page Array. As shown in the "Full Code for the example", this loop is inserted in-between the two existing loops. As a matter of convenience and good coding practices, the inner field properties loop is bundled into a function. As you begin to build larger and more complex menus, you should always place cohesive sections of code like this into separate functions.
A Recursive Example:
For creating a menu from a tree, this function is written as a single loop that builds one layer of the menu hierarchy. For each tree node that has children the function calls itself, effectively creating a nested loop. The leaf nodes provide the stop condition. Since a leaf doesn't have any children, the function returns rather than calling itself.
In the following code we build a menu of the Acrobat Menu Items. This may seem a little redundant, but its' an excellent example and it has it's uses. In this example the function BuildMenuMenu is passed an array of menu items from which it builds an array of Menu Item Objects. As it's doing this it calls itself for each array of child menus it encounters in the current item it's processing. In this particular case the object passed into BuildMenuMenu may be a single Menu Item rather than an array. To deal with this situation and improve the quality and readability of the code the recursive call is broken into two functions. However, it could have easily been written as one function.
With only small variations this code can be used to walk any regular tree. A regular tree is one where all nodes have the same properties.
A Non-Standard Example:
Not all situations are easily put into a menu format, but with a little ingenuity there is usually a way. This next example shows just such a case. This script displays the current size of a document page in inches and allows the user to resize one of its edges.
//** Aquire the crop
box for the current page
This menu is useful just
for the convenient way it displays the page dimensions.
Much could be done to make this example more powerful.
For example, adding another layer to include the sizes of all
the page boxes. Below is a link to the full code, excuted
from a toolbar button.
We hope this material was helpful to you. If you have any questions or comments for us please send email to firstname.lastname@example.org.