Softimage SDK Programmer's Guide > Customizing Softimage using the SDK > Standard and Contextual Menus >

# Custom Menu Examples

The following custom menu examples are provided:

- JScript Example: Nesting SubMenus within SubMenus —demonstrates how to use the AddItem method to create submenus that cascade down through three or four levels.

- JScript Example: Simulating Command Instances with Menu Callback Items —demonstrates how you can define a number of callback menu items in a self-installing plug-in that implement different flavors (instances) of standard Softimage commands.

Tip

In addition, there is a Simple Menu plug-in example (versions written in VBScript, JScript and C++) included with the Softimage SDK installation: check the following location:

%XSI_ROOT%/XSISDK/examples/menus

## JScript Example: Nesting SubMenus within SubMenus

This self-installing menu plug-in demonstrates how to nest submenus deeply. he entries used are just blank entries to demonstrate the mechanism.

    /* ------------------------------------------------------------------

    		This self-installing menu plug-in demonstrates how to
    		nest submenus deeply. The entries used are just blank
    		entries to demonstrate the mechanism.

    */

    /* ------------------------------------------------------------------

    	### REGISTRATION ###

    */
    function XSILoadPlugin( in_reg )
    {
    	// Set up some basic information for the whole plug-in
    	in_reg.Name = "Nested Demo";
    	in_reg.Author = "Softimage SDK Education";
    	in_reg.Email = "editors@softimage.com";
    	in_reg.URL = "http://www.softimage.com/education";
    	in_reg.Major = 1;
    	in_reg.Minor = 0;

    	// Register a single Menu entry point on the Help menu
    	in_reg.RegisterMenu( siMenuMainHelpID, "NestedMenus" );

    	// Only the first level is flat; the submenus still cascade on
    	// the Window menu
    	in_reg.RegisterMenu( siMenuMainWindowID, "NestedMenus", false );

    	// This one will stick all submenus on the same level on the Layers
    	// contextual menu
    	in_reg.RegisterMenu( siMenuSELayersContextID, "FlatMenus" );

    	// You can also log a message about this plug-in's installation
    	Application.LogMessage( "The 'Nested Demo' plug-in has been "
    		+ "successfully installed." );

    	// Finish with success notification
    	return true;
    }

    /* ------------------------------------------------------------------

    	### DEFINITION ###

    	This menu definition creates a cascading menu effect
    	with several submenus nested inside the next one.

    */
    function NestedMenus_Init( in_context )
    {
    	// Get the menu object from the Context input
    	var oTopMnu = in_context.Source;

    	// Add some regular menu items
    	oTopMnu.AddItem( "Regular Item1", siMenuItem );
    	oTopMnu.AddItem( "Regular Item2", siMenuItem );

    	// Add the submenu item
    	var oSubLevel1 = oTopMnu.AddItem( "SubMenu Demo Level1",
    		siMenuItemSubmenu )
    	oSubLevel1.AddItem( "SubLevel1 ItemA", siMenuItem );
    	oSubLevel1.AddItem( "SubLevel1 ItemB", siMenuItem );

    	// Nest another submenu item
    	var oSubLevel2 = oSubLevel1.AddItem( "SubMenu Demo Level2",
    		siMenuItemSubmenu )
    	oSubLevel2.AddItem( "SubLevel2 ItemA", siMenuItem );
    	oSubLevel2.AddItem( "SubLevel2 ItemB", siMenuItem );
    	oSubLevel2.AddItem( "SubLevel2 ItemC", siMenuItem );
    	oSubLevel2.AddItem( "SubLevel2 ItemD", siMenuItem );

    	// ...and another ...
    	var oSubLevel3 = oSubLevel2.AddItem( "SubMenu Demo Level3",
    		siMenuItemSubmenu )
    	oSubLevel3.AddItem( "SubLevel3 ItemA", siMenuItem );
    	oSubLevel3.AddItem( "SubLevel3 ItemB", siMenuItem );
    	oSubLevel2.AddItem( "SubLevel2 ItemC", siMenuItem );

    	// ,,, etc.

    	// Finish with success notification
    	return true;
    }

    /* ------------------------------------------------------------------

    	### DEFINITION ###

    	This menu definition creates a several submenus
    	appearing as siblings.

    */
    function FlatMenus_Init( in_context )
    {
    	// Get the menu object from the Context input
    	var oTopMnu = in_context.Source;

    	// Add some regular menu items
    	oTopMnu.AddItem( "Regular Item1", siMenuItem );
    	oTopMnu.AddItem( "Regular Item2", siMenuItem );

    	// Add a submenu item
    	var oSubLevel1 = oTopMnu.AddItem( "SubMenu Demo Level1",
    		siMenuItemSubmenu )
    	oSubLevel1.AddItem( "SubLevel1 ItemA", siMenuItem );
    	oSubLevel1.AddItem( "SubLevel1 ItemB", siMenuItem );

    	// And another submenu item
    	var oSubLevel2 = oTopMnu.AddItem( "SubMenu Demo Level2",
    		siMenuItemSubmenu )
    	oSubLevel2.AddItem( "SubLevel2 ItemA", siMenuItem );
    	oSubLevel2.AddItem( "SubLevel2 ItemB", siMenuItem );
    	oSubLevel2.AddItem( "SubLevel2 ItemC", siMenuItem );
    	oSubLevel2.AddItem( "SubLevel2 ItemD", siMenuItem );

    	// ...and another ...
    	var oSubLevel3 = oTopMnu.AddItem( "SubMenu Demo Level3",
    		siMenuItemSubmenu )
    	oSubLevel3.AddItem( "SubLevel3 ItemA", siMenuItem );
    	oSubLevel3.AddItem( "SubLevel3 ItemB", siMenuItem );
    	oSubLevel2.AddItem( "SubLevel2 ItemC", siMenuItem );

    	// ,,, etc.

    	// Finish with success notification
    	return true;
    }

## JScript Example: Simulating Command Instances with Menu Callback Items

This example demonstrates how you can define a number of callback menu items in a self-installing plug-in that implement different flavors (instances) of standard Softimage commands:

1. Create a script file in the $user/Application/Plugins folder and name it OpMenuInstances.js . This will contain the JScript implementation of the new custom menu.

2. Set up the registration callback containing two custom menus to appear in the Application menu: one containing Deform operator instances and another containing Generator operator instances:

       function XSILoadPlugin( in_reg )
       {
       	// Register the plug-in information
       	in_reg.Author = "Softimage Corp.";
       	in_reg.Name = "Submenu command example";

       	// Set the version number of this plugin
       	in_reg.Major = 1;
       	in_reg.Minor = 0 ;

       	// Register the Deform menu in the Application menu
       	in_reg.RegisterMenu( siMenuMainApplicationID, "ApplyDeformOp_Menu" );

       	// Register the Generator menu in the Application menu
       	in_reg.RegisterMenu( siMenuMainApplicationID, "ApplyGenOp_Menu" );

       	LogMessage( in_reg.Name + " has been loaded." );

       	return true;
       }

3. Define the Apply Deform Operators menu using the Init callback. This menu's items will always appear, regardless of what is currently selected. It contains three sections separated by menu separator bars:

       function ApplyDeformOp_Menu_Init( in_ctxt )
       {
       	var menu = in_ctxt.source;

       	menu.Name = "Apply Deform Operators";
       	menu.AddCallbackItem( "Twist", "OnApplyDeformOp" );
       	menu.AddCallbackItem( "Bend", "OnApplyDeformOp" );
       	menu.AddCallbackItem( "Bulge", "OnApplyDeformOp" );
       	menu.AddCallbackItem( "Shear", "OnApplyDeformOp" );
       	menu.AddCallbackItem( "Taper", "OnApplyDeformOp" );

       	menu.AddSeparatorItem();

       	menu.AddCallbackItem( "Expand", "OnApplyDeformOp2" );
       	menu.AddCallbackItem( "Contract", "OnApplyDeformOp2" );

       	menu.AddSeparatorItem();

       	menu.AddCallbackItem( "Randomize", "OnApplyDeformOp2" );
       	menu.AddCallbackItem( "Relax", "OnApplyDeformOp2" );
       	menu.AddCallbackItem( "Smooth", "OnApplyDeformOp2" );
       	menu.AddCallbackItem( "QStretch", "OnApplyDeformOp2" );

       	return true;
       }

   This menu uses two callbacks:

   - OnApplyDeformOp—simple implementation that takes the name of the operator (used as the name of the menu item) and calls the ApplyOp command with all default values.

   - OnApplyDeformOp2—more complex implementation that uses a switch statement to test the name of the menu item. Based on the name, either ApplyOp or ApplyKinematicOp is called with specific parameter values and, in some cases, extra tasks are performed after the operator is applied.

4. Write the simple implementation for the OnApplyDeformOp callback:

       function OnApplyDeformOp( in_ctxt )
       {
       	// Get the menu item name
       	var mnu_itm = in_ctxt.Source;

       	ApplyOp( mnu_itm.Name );
       	return true;
       }

5. Write the more complex implementation for the OnApplyDeformOp2 callback:

       function OnApplyDeformOp2( in_ctxt )
       {
       	// Get the menu item name
       	var mnu_itm = in_ctxt.Source;

       	switch (mnu_itm.Name) {
       		case "Randomize" :
       			// You can specify extra arguments
       			ApplyOp( "Randomize", null, siBranch );
       			break;
       		case "Expand" :
       			// You could run extra commands to tweak the result...
       			var op = ApplyOp( "Push" )(0);
       			op.ampl.Value = 2;
       			break;
       		case "Contract" :
       			// ...and make slight changes
       			var op = ApplyOp( "Push" )(0);
       			op.ampl.Value = -2;
       			break;
       		case "QStretch" :
       			// You can call a different command
       			ApplyKinematicOp( "QStretch", null, siBranch );
       			break;
       		case "Punch Out Shape" :
       			ApplyOp( "QStretch", null, siBranch );
       			break;
       		default :
       			// This will catch "Relax" and "Smooth"
       			ApplyOp( mnu_itm.Name );
       	}

       	return true;
       }

6. Define the Apply Generator Operators menu using the Init callback. This menu's items appear only under certain circumstances related to what is currently selected:

   - When the selected items have different primitive types, no items are added to the menu.

   - When two or more surface meshes are selected, only the Fit and Fillet Intersection operators are added to the menu.

   - When only one polygon mesh is selected, only the Subdivide operator is added to the menu.

   - When two or more polygon meshes are selected, the three Boolean, Blend and Subdivide operators are added to the menu.

   Important

   This conditional menu contruction only works when the menu is dynamic (that is, when the RegisterMenu (PluginRegistrar) method uses the default value, true, for the Dynamic argument—see Are the Contents of My Menu Fixed? for more information).

       function ApplyGenOp_Menu_Init( in_ctxt )
       {
       	var menu = in_ctxt.source;

       	menu.Name = "Apply Generator Operators";

       	// Make all selected objects are the same type
       	var primtype = "";
       	for ( var i=0; i<Selection.Count; i++ ) {
       		if ( primtype == "" ) {
       			primtype = Selection(i).Type;
       		} else {
       			if ( primtype != Selection(i).Type ) {
       				return false;
       			}
       		}
       	}

       	// Build the menu items according to the primitive type of the
       	// selected objects
       	switch (primtype) {
       		case siSrfMeshPrimType :
       			// Only display operator types that can be applied to surfmeshes
       			if ( Selection.Count > 1 ) {
       				// Make sure at least two items are selected
       				menu.AddCallbackItem( "Fit to Shape", "OnApplyGenOp" );
       				menu.AddCallbackItem( "Fillet Intersection", "OnApplyGenOp" );
       			}
       			break;

       		case siPolyMeshType :
       			// Only display operator types that can be applied to polymeshes
       			if ( Selection.Count > 1 ) {
       				// Make sure at least two items are selected for boolean operations
       				menu.AddCallbackItem( "Punch Out Shape (Difference)",
       					"OnApplyGenOp" );
       				menu.AddCallbackItem( "Leave Remainder Shape (Intersection)",
       					"OnApplyGenOp" );
       				menu.AddCallbackItem( "Bind Shapes Together (Union)",
       					"OnApplyGenOp" );
       				menu.AddCallbackItem( "Blend Shapes (Mesh)", "OnApplyGenOp" );
       			}

       			menu.AddCallbackItem( "Subdivide Selection", "OnApplyGenOp" );
       			break;
       	}

       	return true;
       }

7. Write the implementation for the OnApplyGenOp callback. Since the Init callback performed all the tests on the selection before enabling certain callbacks, this callback doesn't need to check that the selected types are appropriate for the input objects, since any commands that didn't fit the selection were filtered out:

       function OnApplyGenOp( in_ctxt )
       {
       	// Get the menu item name
       	var mnu_itm = in_ctxt.Source;

       	switch (mnu_itm.Name) {
       		case "Punch Out Shape (Difference)" :
       			ApplyGenOp( "BooleanGenDifference", "", "", null,
       				siImmediateOperation, siDeleteGenOpInputs );
       			break;
       		case "Leave Remainder Shape (Intersection)" :
       			ApplyGenOp( "BooleanGenIntersection", "", "", null,
       				siImmediateOperation, siDeleteGenOpInputs );
       			break;
       		case "Bind Shapes Together (Union)" :
       			ApplyGenOp( "BooleanGenUnion", "", "", null,
       				siImmediateOperation, siDeleteGenOpInputs );
       			break;
       		case "Subdivide Selection" :
       			MeshSubdivideWithCenter( null, "", null,
       				siImmediateOperation, siDeleteGenOpInputs );
       			break;
       		case "Fit to Shape" :
       			ApplyGenOp( "SrfFit", "", "", null,
       				siImmediateOperation, siDeleteGenOpInputs );
       			break;
       		case "Fillet Intersection" :
       			ApplyGenOp( null, "", "", null,
       				siImmediateOperation, siDeleteGenOpInputs );
       			break;
       		case "Blend Shapes (Mesh)" :
       			ApplyGenOp( "MeshBlend", "", "", null,
       				siImmediateOperation, siDeleteGenOpInputs );
       			break;
       		default :
       	}

       	return true;
       }

8. Save the file and launch Softimage. If Softimage is already running, you can explicitly load this plug-in by opening the Plug-in Manager (select Plug-ins Manager from the File menu) and clicking the Update button.

9. To test the menus, create and select some primitives and open the Application menu. At the bottom you will see the new menus:

   - Apply Deform Operators [u]

   - Apply Generator Operators [u]