The XMLSpyFormEditor

xml spy
The XMLSpyFormEditor

XMLSpyFormEditor is the scripting environment of XMLSpy. It gives you the ability to create, manage and store your own forms, macros and event handlers.

Before you can start using the scripting environment, you must download it from the Component Download Center on our web site http://www.xmlspy.com. Afterwards you can start the installation.

To invoke the scripting environment select "Tools | Switch to scripting environment". There is a predefined global scripting project active the first time you start the form editor. This project is installed with the scripting component of XMLSpy and contains a sample that interacts with the Altova.xml file.

The screen shot below shows the "FormDate" form and the implementation of the On_BeforeStartEditing event handler. Both are part of the provided Altova sample.

The graphic also shows the main parts of the XMLSpyFormEditor. The "Project window" (left), the "Property Sheet" (left), the client area with any open forms or modules (in the middle) and the "Tool Bar" on the right.

To test the sample, switch back to XMLSpy and open the file Altova.xml. Activate the Enhanced Grid View and try to start editing the "/a:Altova/Division/Established" (XPath) element.

Project Window
The Project window gives you access to the forms and modules of a all open scripting projects.
There is always one active scripting project. The title of the active project is printed in bold. Several commands of the File and Project menus refer to the currently active project only.

Two context-sensitive popup menus are available within the Project Bar. The first appears if you right-click the title of a project, and the second if you click a form or module. These popup menus gives you access to functions also available in the "File" and "Project" menus in the menu bar.

The Property Sheet

The Property Sheet lists all properties and events of the selected user interface element (buttons, list-boxes, ActiveX controls, etc.) of the active form.

It gives you the ability to modify these properties and to add code to the empty event handlers. The combo-box at the top lists all controls of the form.

The pictures below shows the three pages of the Microsoft DatePicker ActiveX control. Please note that different controls have different properties, and not all controls have three tabs in the Property Sheet.

The name of a control is specified by the (ObjectCode) property (here MScomCtl21). If you write some scripting code and need access to methods or properties of the control, you must use this name. Example:

   var strTmp;
   strTmp = "";
   strTmp = MSComCtl21.Year + '-' + MSComCtl21.Month + '-' + MSComCtl21.Day;

The "How to create a form" page gives you an example on adding some code to the click-event of a control.

The Tool Bar
Use the Tool Bar to add controls to your forms. Select a control from the bar and draw a rectangle in your form, to set its place and size.

The most common controls from the Tool Bar are:

Static Text - Adds text fields like captions or field descriptions.

Button - Adds a button to your form. It is possible to assign bitmaps to these buttons.

CheckBox - Enables Yes-No style elements

ComboBox - Gives the user the ability to enter text or to select an entry from the drop-down list.

ListBox - Displays a list of items for selection.

EditBox - Defines a single line of text.

MultiEditBox - Defines multiple lines of text.

ActiveX - Allows the integration of an ActiveX control in the form.

All of these controls have their own properties and events. Select a control in your form and use the Property Sheet to modify the properties and add some scripting code to the provided events.

Top Next

© 2002 Altova


Main scripting features

Main scripting features

With the introduction of the COM API, the possibilities of XMLSpy became substantially expanded. It is the combination of common programming techniques using functions, recursions, global variables, error handling and so on, that gives the user the ability to deal with complex problems, to automate repetitive jobs or to realize completely new tasks. The scripting environment is the tool for creating and organizing these scripts.

Macros
Macros are used to implement complex or repetitive tasks and for direct user input via forms (dialogs). Macros don't have parameters and return values. It is possible to access all variables and functions which are declared in the global declarations and to display forms. Please see "How to write a macro" for a simple example. See also "Calling macros from XMLSpy".

Events
Events are a powerful way to react to user input and modify the behaviour of XMLSpy. All scripting code of an event is executed immediately, and gives you the ability to directly influence on the users handling of XMLSpy.

Most events have parameters which inform the script about the currently modified data. The return value informs the application how to continue its own processing (eg. forbid editing). See How to handle an event to learn more about the implementation of events.

Forms
Dialog boxes enable your scripts and XMLSpy to interact with the user. You are free to add any type of control to your forms. These controls use their own event handlers for the implementation of their functionality. A list of supported events for the selected control appears in the Property Sheet on the "Events" page. See "How to create a form" for a description of creating forms.

Programming details

1. Because macros don't have parameters or return values, a function header should not exist in the macro implementation.

2. Event handlers need function headers with the correct spelling of the event name, or the event handler won't get called.

3. It is possible to declare local variables and helper functions within macros and event handlers.
Example:

   //return value: true allows editing
   //              false forbids editing

   var txtLocal;

   function Helper()
   {
      txtMessage = txtLocal;
      Application.ShowForm("MsgBox");
   }

   function On_BeforeStartEditing(objXMLData)
   {
      txtLocal = "On_BeforeStartEditing()";
      Helper();
   }

4. Recursive functions are supported. The function DeleteXMLElements() from "How to create a Form" calls itself recursively.

5. A form can display another form with the function Application.ShowForm().

6. Out-parameters from methods of the XMLSpy API require special variables in JavaScript.
Example:

   // use JavaScript method to access out-parameters
   var strError = new Array(1);
   var nErrorPos = new Array(1);
   var objBadData = new Array(1);

   bOK = objDoc.IsValid(strError,nErrorPos,objBadData);

7. While a macro is executed, event handlers from XMLSpy are not processed.

Previous Top Next

© 2002 Altova


Calling macros from XMLSpy

Calling macros from XMLSpy

Once you have written your own macros (see also "How to write a macro"), you will want to execute them from XMLSpy. There are two ways to run macros.

Calling macros from the "Show macros..." dialog
To show this dialog select "Show macros..." from the "Tools" menu.

The dialog displays a list of all macros defined in the global scripting project and in the scripting project associated with the current project opened in XMLSpy.

Additionally, there is a "Run" button to call any selected macro. Before the macro is executed, the "Show macros..." dialog is closed.

The "Project" - list box lets you switch between the global scripting project and the project scripts.

Calling macros from the "Tools" menu
The XMLSpy API includes a function to add menu items with associated macros to the "Tools" menu.

To add a menu item call the XMLSpy API function AddMacroMenuItem().

To reset the "Tools" menu call ClearMacroMenu(). This removes all previously added menu items. These commands will generally be called from the "Autorun" macro of the global scripting project, or during the "On_OpenProject" event.

Example:

Application.AddMacroMenuItem("DeleteElements","Delete Elements Dialog");

The first parameter is the name of the macro. If you run the macro and there is an open project with scripts associated to it, XMLSpy searches for the macro in the project scripts first. If there are no project scripts, or XMLSpy can't find the macro, it tries to find the macro in the global scripts.

The second parameter is the display text for the menu item.


Projects and Scripts

Projects and Scripts

It is possible to assign scripts to a normal XMLSpy project.

The assigned scripts will be loaded each time XMLSpy opens the project. The scripting project is closed when XMLSpy closes its project.

To assign and unassign scripts to a project, use the menu items from the "Project" submenu of the "Tools" menu. These menu items are not active if there is no currently active XMLSpy project.

To assign a script, select the "Assign Scripts to Project" menu item. A standard file dialog appears enabling you to choose the scripting project file. The scripts are loaded and you are able to edit them if you switch to the scripting environment.

If you want to see the path of a scripting project after you have assigned it, select "Project Information" from the "Project" menu of the XMLSpyFormEditor.

To unassign the current scripts from the project select "Unassign Scripts from Project". XMLSpyFormEditor closes the scripting project immediately.

It is possible to temporarily deactivate the project scripts with the "Project Scripts active" menu item. The associated scripting project file will not be unassigned.

Any forms, macros and events which are defined in the project scripts, "override" any existing definitions from the global scripts.

Example:
If the event handler for On_BeginEditing() exists in the project and global scripts, and XMLSpy raises this event, the event handler from the project scripts will be called.

You have to remove the predefined (but empty) event handlers from the project scripts manually, if you want the event handlers from the global scripts to get called. This is also true for the ShowForm() method of the Application object, to show a form and the AddMacroMenuItem() method to add a macro to the "Tools" menu.


Scripting Settings

Scripting Settings

The graphic below shows the settings page for the scripting environment of XMLSpy:

Activate scripting environment when XMLSpy starts
If this checkbox is not selected, the scripting environment is not activated until the user selects "Tools | Switch to scripting environment" for the first time.

Global scripting project file
This property sets the global scripting project file. If this file does not exist when the scripting environment starts, a new project will be created.

Run auto-macros
Enables or disables the execution of auto-macros. Currently "Autorun" from the global scripts is the only supported automatic macro. "Autorun" executes when the scripting environment starts.

Process events
Enables or disables the execution of event handlers.

Standard script language
A new global scripting project created from XMLSpy will use this scripting language.


How to write a macro

How to write a macro

The goal of this tutorial is to write a macro that removes all namespace prefixes from the active document. To add a macro you have to switch to the XMLSpyFormEditor. Use "Tools | Switch to scripting environment" to show the main window of the form editor. If the scripting environment was not installed before, you have to do it now.

Open the XMLSpyMacros module with a double-click on the tree item in the project window. Use the "Add Function" command either from the "Project" menu, or from the popup menu which appears if you right-click the module in the project bar:

Type in "RemoveNamespaces" for the macro name in the following dialog. XMLSpyFormEditor creates a new macro and adds it to the (XMLSpyMacros) module.

Note that macros don't support parameters or return values. There is therefore no function header. Enter the code below in the macros window:

   if(Application.ActiveDocument != null)   {
      RemoveAllNamespaces(Application.ActiveDocument.RootElement);
      Application.ActiveDocument.UpdateViews();
   }

We now have to write the RemoveNamespaces function. Place this function into the global declarations module, so that it is accessible from our macros, events and forms.

Activate the (GlobalDeclarations) module and add the RemoveNamespaces() function to any previously defined global variables and functions as follows:

   function RemoveAllNamespaces(objXMLData)
   {
      if(objXMLData == null)
         return;

      if(objXMLData.HasChildren)   {
         var objChild;

         // spyXMLDataElement := 4
         objChild = objXMLData.GetFirstChild(4);

         while(objChild)   {
            RemoveAllNamespaces(objChild);

            try   {
               var nPos,txtName;
               txtName = objChild.Name;

               if((nPos = txtName.indexOf(":")) >= 0)   {
                  objChild.Name = txtName.substring(nPos+1);
               }

               objChild = objXMLData.GetNextChild();
            }
            catch(Err)   {
               objChild = null;
            }
         }
      }
   }

This completes the creation of the new macro. You can run it from the "Show macros..." dialog in the "Tools" menu of XMLSpy.

The sample can be easily extended to perform renaming instead of removing of the namespaces. Design a form (see also "How to create a form") where the user can specify the old and the new namespace names. Then change the try-catch-block of the RemoveAllNamespaces() function to something like this:

   try   {
      var nPos,txtName;
      txtName = objChild.Name;

      if((nPos = txtName.indexOf(":")) >= 0)   {
         var txtOld;
         txtOld = txtName.substring(0,nPos);

         if(txtOld == txtOldNamespace)
            objChild.Name = txtNewNamespace + ":" +             txtName.substring(nPos+1);
      }

      objChild = objXMLData.GetNextChild();
   }
   catch(Err)   {
      objChild = null;
   }

This code assumes that txtOldNamespace and txtNewNamespace are declared as globals and are set with the proper values.


How to handle an event

How to handle an event

To handle an event you have to switch to the XMLSpyFormEditor. Use "Tools | Switch to scripting environment" to open the main window of the form editor.

Open the (XMLSpyEvents) module with a double-click at the tree item in the project bar.

Normally all events should be predefined in this module. If the event you want to write code for does not have an entry in the event list box, use the "Add Function" command either from the "Project" menu, or from the popup menu which appears if you right-click the module in the project bar to create it again.

Note that you must use the exact same spelling as was used when XMLSpyFormEditor created the new project and inserted the predefined event handlers for you. You must also fill in possible parameters into the function header. The best way to recreate a deleted event handler is to create a new scripting project and copy name and function definition from there.

The screen shot shows you the (XMLSpyEvents) module and the list of predefined events. Choose On_OpenProject to add some scripting code which will be executed each time XMLSpy opens a project.

The script sequentially opens all XML files located in the XML folder of the project and validates them. If the validation fails the scripts stops and shows the validation error. If the file passes the validation it will be closed.

We now need to add some code to the On_OpenProject event handler:

function On_OpenProject()
{
   var   bOK;
   var   nIndex,nCount;
   var   objItems,objXMLFolder = null;

   objItems = Application.CurrentProject.RootItems;
   nCount = objItems.Count;

   // search for XML folder
   for(nIndex = 0;nIndex < nCount;nIndex++)   {
      var txtExtensions;
      txtExtensions = objItems.Item(nIndex).FileExtensions;

      if(txtExtensions.indexOf("xml") >= 0)   {
         objXMLFolder = objItems.Item(nIndex);
         break;
      }
   }

   // does XML folder exist?
   if(objXMLFolder)   {
      var objChild,objDoc;

      nCount = objXMLFolder.ChildItems.Count;

      // step through associated xml files
      for(nIndex = 1;nIndex <= nCount;nIndex++)   {
         objChild = objXMLFolder.ChildItems.Item(nIndex);

         try   {
            objDoc = objChild.Open();

            // use JScript method to access out-parameters
            var strError = new Array(1);
            var nErrorPos = new Array(1);
            var objBadData = new Array(1);

            bOK = objDoc.IsValid(strError,nErrorPos,objBadData);

            if(!bOK)   {
               // if the validation fails, we should display the
               // message from XMLSpy
               // of course we have to create the form "MsgBox" and
               // define the global txtMessage variable
               //
               // txtMessage = Position:" + nErrorPos[0] + "\n" +             //          strError[0];
               // txtMessage +=   "\n\nXML:\n" + objBadData[0].Name + ", " +
               //         objBadData[0].TextValue;
               //
               // Application.ShowForm("MsgBox");

               break;
            }

            objDoc.Close(true);
            objDoc = null;
         }
         catch(Err)   {
            // displaying the error description here is a good idea

            // txtMessage = Err.Description;
            // Application.ShowForm("MsgBox");

            break;
         }
      }
   }
}

That's all. Switch to XMLSpy, open a project and see what happens.


How to create a Form

How to create a Form

This section explains how to create a new Form in the XMLSpyFormEditor and how to display it in XMLSpy. The sample builds a dialog where the user can type in the name of elements that are to be deleted from the active XML file in XMLSpy.

To create a form, switch to the XMLSpyFormEditor. Use "Tools | Switch to scripting environment" to open the main window of the form editor. There is at least one project open, the global project.

There are two ways for adding a new form to a project. You can use the "Add Form" command from the "Project" menu, or click the "New Form" Button in the project window. In both cases, the new form is added immediately and appears as a separate window.

The screen shot shows the XMLSpyFormEditor right after the creation of a new form:

You could change the name of the new form now. To do so, enter a new name in the Property Sheet in the line (FormCode). For our example type in "RemoveDlg". Type in the form-title you like in the "Title" line of the Property Sheet.

We then insert an edit box where the user can type in the name of the elements to remove from the XML file. Select the edit box icon [image] from the tool bar on the right side of the main window. Define a rectangle with the mouse for the edit box in the display area of our RemoveDlg form.

In the (ObjectCode) line of the Property Sheet, type in "EditElements" as the name of the new edit box. Add a caption for the edit box with the static text icon [image] from the tool bar. The edit window of the form should look like this:

We now need two buttons in our form, "Delete" and "Cancel". Use the button icon [image] to draw them in our form window. Type in "BtnDelete" and "BtnCancel" as names in the (ObjectCode) lines of the button properties.

Set the button text in the Text fields to "Delete" and "Cancel". Additionally set the type of the Cancel - Button to "0 - OK" in the ButtonType field of the properties.

After we have designed our form we need some scripting code to make it useful.

We need a function that recursively steps through the XML file, and deletes all XMLData objects with a given name. We declare this in the (GlobalDeclarations) module of the project. If the module is not visible, double-click it in the project bar. Type in the following code:

   var txtElementName;

   function DeleteXMLElements(objXMLData)
   {
      if(objXMLData == null)
         return;

      if(objXMLData.HasChildren)   {
         var objChild;
         objChild = objXMLData.GetFirstChild(-1);

         while(objChild)   {
            DeleteXMLElements(objChild);

            try   {
               if(objChild.Name == txtElementName)
                  objXMLData.EraseCurrentChild();

               objChild = objXMLData.GetNextChild();
            }
            catch(Err)   {
               objChild = null;
            }
         }
      }
   }

After this your global declarations module should look like this:

Select the BtnDelete button and choose the "Events" tab from the property sheet. Click on the edit code icon [image] in the "EventClick" line of the tab. The Mini-Editor window pops up with the predefined function header of the click-event. This function will be executed every time the user clicks the "Delete" button. Add the following code to the event:

   if(EditElements.Text != "")   {
      txtElementName = EditElements.Text;
      DeleteXMLElements(Application.ActiveDocument.RootElement);
   }

The Delete - Button sets the global variable txtElementName to the string from the edit box and calls DeleteXMLElements() with the root element of the active document.

There is not much left to do.
Select the (XMLSpyMacros) module, create a new macro (see also "How to write a macro") called "DeleteElements" and enter this code:

   var a;

   if(Application.ActiveDocument != null)
      a = Application.ShowForm("RemoveDlg");

Run the macro from the "Show macros..." dialog of the "Tools" menu within XMLSpy.

Previous Top

© 2002 Altova