StarOffice 7 (Win32) Forms Frank Schönheit 2002-01-25T09:18:48 Frank Schönheit 2004-08-18T15:18:01 en-US 653 P5DT19H50M40S 627458 1789 20736 20687 true false false false view2 14499 636030 1789 627458 22523 648143 0 110 false false false 1 zh CN ﹀﹂﹄﹏、~¢々‖•·ˇˉ―--′ .([{£¥'"‵〈《「『【〔〖([{£¥〝︵︷︹︻︽︿﹁﹃﹙﹛﹝({ 0 false false false false true true true true BAT+/0ZpbmVQcmludCAyMDAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAARmluZVByaW50IDIwMDAAAAAAAAAAAAAAAAAAAAAAAAAWAAEASgMAAAAAAAAFAFZUAAAkbQAAM1ROVwEACABGaW5lUHJpbnQgMjAwMAAAAAAAAAAAAAAAAAAAAAAAAAEESAScAKYCA24HAAEAAQAAAAAAZAABAAEAWAICAAAAWAIDAAAATGV0dGVyAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABYAhgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABGUAIAcwBvAGYAZgBpAGMAZQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAL8aAABYAgAAAAAAAAEAAQAAADEAQwBvAHUAcgBpAGUAcgAgAE4AZQB3AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA= 0 true true false false false false Adreßbuch FinePrint 2000 true false false false true true 0 disabled true true adressen true false 19 Product name and Release number Manual Title  March 2001 Chapter 19 19 Product name and Release number Manual Title  March 2001 19 19 Product name and Release number Manual Title  March 2001 19 19 19 19 Product name and Release number Manual Title 19 Product name and Release number Manual Title Contents 19 Contents 19 19 Product name and Release number Manual Title  March 2001 Index 19 Chapter 13 19 19 Index 19 Chapter 13 Forms 19 19 Product name and Release number Manual Title  March 2001 Forms Introduction Forms offer a method of control-based data input. A form or form document consists of a set of controls, where each one enters a single piece of data. In a simple case, this could be a plain text field allowing you to insert some text without any word breaks. When we speak of forms, we mean forms and controls, because these cannot be divided. If an internet site asks you for information, for example, for a product registration you are presented with fields to enter your name, your address and other information. These are HTML forms. Basically, this is what [PRODUCTNAME] forms do. They enhance nearly every document with controls for data input. This additional functionality put into a document is called the form layer within the scope of this chapter. The most basic functionality provides the controls for HTML form documents mentioned above: If you open an HTML document with form elements in [PRODUCTNAME] Writer, these elements are represented by components from [MODULE:com.sun.star.form]. The more enhanced functionality provides support for data-aware forms. These are forms and controls that are bound to a data source registered in [PRODUCTNAME] to enter data into tables of a database. For more information about data sources and data access in general, refer to the [CHAPTER:Database]. When discussing forms, the difference between form documents and logical forms have to be distnguished. The form document refers to a document as a whole, and logical forms is a logical concept, basically a set of controls with additional properties. See below for details.Within the scope of this chapter, when a "form" is referred to, we mean the logical form. The logical form is more interesting from the API programmer's perspective. Models and Views The Model-View Paradigm A basic concept to understand about forms and controls in [PRODUCTNAME] is the model-view paradigm. For a given element in your document,for example, a text field in your HTML form, it says that you have exactly one model and an arbitrary number of views. The model is what is part of your document in that it describes how this element looks , and how it behaves. The model even exists when you do not have an open instance of your document. If it is stored in a file, the file contains a description of the model of your element. Note graphics marks a special text section In UNO, the simplest conceivable model is a component implementing [IDL:com.sun.star.beans.XPropertySet] only. Every aspect of the view could then be described by a single property. In fact, as you will see later, models for form controls are basically property sets. The view is a visual representation of your model. It is the component which looks and behaves according to the requirements of the model. You can have multiple views for one model, and they would all look alike as the model describes it. The view is visible to the user. It is for visualizing the model and handles interactions with the user. The model, however, is merely a "dumb" container of data. A good example to illustrate this is available in [PRODUCTNAME]. Open an arbitrary document and choose the menu item Window - New Window. A second window is opened showing the same document displayed in the first window. This does not mean that the document was opened twice, it means you opened a second view of the same document, which is a difference. In particular, if you type some text in one of the windows, this change is visible in both windows. That is what the model-view paradigm is about: Keep your document data once in the model, and when you need to visualize the data to the user, or need interaction from the user that modifies the document, create views to the model as needed. Between model and view a 1:n relationship exists: Grpahic showing the relation between Model and View Illustration 13.1 Note graphics marks a special text section Note that the relation is directed. Usually, a view knows its model, but the model itself does not know about the views which visualize it. Models and Views for Form Controls [TOPIC:com.sun.star.awt.UnoControl;com.sun.star.awt.UnoControlModel]Form controls follow the model-view paradigm. This means if you have a form document that contains a control, there is a model describing the control's behavior and appearance, and a view that is the component the user is sees. Note graphics marks a special text section Note that the term "control" is ambiguous here. Usually, from the user's perspective, it is what is seen in the document. As the model-view paradigm may not be obvious to the user, the user tends to consider the visible representation and the underlying model of the control as one thing, that is, a user who refers to the control usually means the combination of the view and the model.As opposed to the user's perspective, when the UNO API for the form layer refers to a control, this means the view of a form element, if not stated otherwise. The base for the controls and models used in the form layer are found in the module [IDL:com.sun.star.awt], the [IDL:com.sun.star.awt.UnoControl] and [IDL:com.sun.star.awt.UnoControlModel] services. As discussed later, the model hierarchy in [IDL:com.sun.star.form.component] extends the hierarchy of [IDL:com.sun.star.awt], whereas the control hierarchy in [IDL:com.sun.star.form.control] is small. Everything from the model-view interaction for form controls is true for other UNO controls and UNO control models, as well. Another example for components that use the model-view paradigm are the controls and control models in [PRODUCTNAME] Basic dialogs ([CHAPTER:BasicAndDialogs.ProgrammingDialogs.Controls]). Model-View Interaction When a model and a view interoperate, a data transfer in both directions is required, from the model to the view and conversely. Consider a simple text field. The model for a control implements a [IDL:com.sun.star.form.component.TextField] service. This means it has a property Text, containing the current content of the field, and a property BackgroundColor specifying the color that should be used as background when drawing the text of the control. First, if f the value of the BackgroundColor property is changed, the control is notified of the change. This is done by UNO listener mechanisms, such as the [IDL:com.sun.star.beans.XPropertyChangeListener] allowing the control to listen for changes to model properties and react accordingly. Here the control would have to redraw itself using the new background color. In fact this is a common mechanism for the communication between model and view: The view adds itself as listener for any aspect of the model which could affect it, and when it is notified of changes, it adjusts itself to the new model state. This means that the model is always the passive part. The model does not know its views, or at least not as views, but only their role as listeners, while the views know their model. On the other hand, if the view is used for interaction with the user, of the data needs to be propagated from the view to the model. The user enters data in a text field, and the change is reflected in the model. Remember that the user sees the control only, and everything affects the control in the first step. If the user interacts with the view with the intention of modifying the model, the view propagates changes to the model. In our example, the user enters text into the control, the control automatically updates the respective property at the model (Text), thus modifying the document containing the model. Form Layer Views View Modes An important aspect to know when dealing with forms is that the view for a form layer is in different modes. More precise, there is a design mode available, opposite to a live mode. In design mode, you design your form. interactively with [PRODUCTNAME] by inserting new controls, resizing them, and modifying their properties,together with control models and shapes. although [PRODUCTNAME] hides this. In live mode, the controls interact with the user for data input. The live mode is the natural mode for forms views, because usually a form is designed once and used again. The following example switches a given document view between the two modes: [SOURCE:Forms/DocumentViewHelper.java] /** toggles the design mode of the form layer of active view of our sample document */ protected void toggleFormDesignMode() throws java.lang.Exception { // get a dispatcher for the toggle URL URL[] aToggleURL = new URL[] {new URL()}; aToggleURL[0].Complete = new String(".uno:SwitchControlDesignMode"); XDispatch xDispatcher = getDispatcher(aToggleURL); // dispatch the URL - this will result in toggling the mode PropertyValue[] aDummyArgs = new PropertyValue[] {}; xDispatcher.dispatch(aToggleURL[0], aDummyArgs); } The basic idea is to dispatch the URL ".uno:SwitchControlDesignMode" into the current view. This triggers the same functionality as if the button Design Mode On/Off was pressed in [PRODUCTNAME].In fact, SwitchControlDesignMode is the UNO name for the slot triggered by this button. Locating Controls A common task when working with form documents using the [PRODUCTNAME] API is to obtain controls. Given that there is a control model, and a view to the document it belongs to, you may want to know the control that is used to represent the model in that view. This is what the interface [IDL:com.sun.star.view.XControlAccess] at the controller of a document view is made for. [SOURCE:Forms/DocumentViewHelper.java] /** retrieves a control within the current view of a document @param xModel specifies the control model which's control should be located @return the control tied to the model */ public XControl getControl(XControlModel xModel) throws com.sun.star.uno.Exception { XControlAccess xCtrlAcc = (XControlAccess)UnoRuntime.queryInterface( XControlAccess.class , m_xController); // delegate the task of looking for the control return xCtrlAcc.getControl(xModel); } Focussing Controls To focus a specific control in your document, or more precisely, in one of the views of your document: [SOURCE:Forms/DocumentViewHelper.java] /** sets the focus to a specific control @param xModel a control model. The focus is set to that control which is part of our view and associated with the given model. */ public void grabControlFocus(Object xModel) throws com.sun.star.uno.Exception { // look for the control from the current view which belongs to the model XControl xControl = getControl(xModel); // the focus can be set to an XWindow only XWindow xControlWindow = (XWindow)UnoRuntime.queryInterface(Xwindow.class, xControl); // grab the focus xControlWindow.setFocus(); } As you can see, focussing controls is reduced to locating controls. Once you have located the control, the [IDL:com.sun.star.awt.XWindow] interface provides everything needed for focussing. Form Elements in the Document Model The model of a document is the data that is made persistent, so that all form elements are a part of it. Refer to chapter [CHAPTER:OfficeDev.AppEnv.Overview.FrameworkAPI.FCM] for additional information. This is true for logical forms, as well as for control models. Controls , that is, the view part of form elements, are not made persistent, thus are not accessible in the document model. A Hierarchy of Models The components in the form layer are organized hierarchically in an object tree. Their relationship is organized using the standard interfaces, such as [IDL:com.sun.star.container.XChild] and [IDL:com.sun.star.container.XIndexAccess]. As in every tree, there is a root with inner nodes and leaves. There are different components described below that take on one or several of these roles. FormComponent Service The basis for all form related models is the [IDL:com.sun.star.form.FormComponent] service. Its basic characteristics are: it exports the [IDL:com.sun.star.container.XChild] interface it has a property Name it exports the [IDL:com.sun.star.lang.XComponent] interface Form components have a parent and a name, and support lifetime control that the common denominator for form elements and logical forms, as well as for control models. FormComponents Service [TOPIC:com.sun.star.form.FormComponents]In the level above, a single form component is a container for components. Stepping away from the document model, you are looking for a specific form component, such as the model of a control, you pass where all the control models are attached. This is the [IDL:com.sun.star.form.FormComponents] component. The service offers basic container functionality, namely an access to its elements by index or by name), and a possibility to enumerate its elements. Provided that you have a container at hand, the access to its elements is straightforward. For example, assume you want to enumerate all the elements in the container, and apply a specific action for every element. The enumFormComponents() method below does this by recursively enumerating the elements in a [IDL:com.sun.star.form.FormComponents] container. [SOURCE:Forms/FormLayer.java] /** enumerates and prints all the elements in the given container */ public static void enumFormComponents(XNameAccess xContainer, String sPrefix) throws java.lang.Exception { // loop through all the element names String aNames[] = xContainer.getElementNames(); for (int i=0; i<aNames.length; ++i) { // print the child name System.out.println(sPrefix + aNames[i]); // check if it's a FormComponents component itself XServiceInfo xSI = (XServiceInfo)UnoRuntime.queryInterface(XServiceInfo.class, xContainer.getByName(aNames[i])); if (xSI.supportsService("com.sun.star.form.FormComponents")) { // yep, it is // -> step down XNameAccess xChildContainer = (XnameAccess)UnoRuntime.queryInterface( XNameAccess.class, xSI); enumFormComponents(xChildContainer, new String(" ") + sPrefix); } } } /** enumerates and prints all the elements in the given container, together with the container itself */ public static void enumFormComponents(XNameAccess xContainer) throws java.lang.Exception { XNamed xNameAcc = (XNamed)UnoRuntime.queryInterface(XNamed.class, xContainer); String sObjectName = xNameAcc.getName(); System.out.println( new String("enumerating the container named \"") + sObjectName + new String("\"\n")); System.out.println(sObjectName); enumFormComponents(xContainer, " "); } Logical Forms [TOPIC:com.sun.star.form.component.Form]Forms as technical objects are also part of the document model. In contrast to control models, forms do not have a view representation. For every control model, there is a control the user interacts with, and presents the data back to the user. For the form, there is no view component. The basic service for logical forms is [IDL:com.sun.star.form.component.Form]. See below for details regarding this service. For now, we are interested in that it exposes the [IDL:com.sun.star.form.FormComponent] service, as well as the [IDL:com.sun.star.form.FormComponents] service. This means it is part of a form component container, and it is a container. Thus, in our hierarchy of models, it can be any node, such as an inner node having children, that is, other form components,, as well as a leaf node having no children, but a parent container. Of course both of these roles are not exclusive. This is how data aware forms implement master-detail relationships. Refer to the [CHAPTER:Forms.DataAware]. Forms Container In our model hierarchy, we have inner nodes called the logical forms, and the basic element called the form component. As in every tree, our hierarchy has a root, that is, an instance of the [IDL:com.sun.star.form.Forms] service. This is nothing more than an instance of [IDL:com.sun.star.form.FormComponents]. In fact, the differentiation exists for a non-ambiguous runtime instantiation of a root. Note graphics marks a special text section Note that the [IDL:com.sun.star.form.Forms] service does not state that components implementing it are a [IDL:com.sun.star.form.FormComponent]. This means this service acts as a tree root only, opposite to a [IDL:com.sun.star.form.Forms] that is a container, as well as an element, thus it can be placed anywhere in the tree. Actually, it is not necessary for external components to instantiate a service directly. Every document has at least one instance of it. A root forms container is tied to a draw page, which is an element of the document model, as well. Refer to [IDL:com.sun.star.drawing.DrawPage]. A page optionally supports the interface [IDL:com.sun.star.form.XFormsSupplier] giving access to the collection. In the current [PRODUCTNAME] implementation, Writer and Calc documents fully support draw pages supplying forms. The following example shows how to obtain a root forms collection, if the document model is known which is denoted with s_aDocument. [SOURCE:Forms/DocumentHelper.java] /** gets the <type scope="com.sun.star.drawing">DrawPage</type> of our sample document */ public static XDrawPage getDocumentDrawPage() throws java.lang.Exception { XDrawPage xReturn; // in case of a Writer document, this is rather easy: simply ask the XDrawPageSupplier XDrawPageSupplier xSuppPage = (XDrawPageSupplier)UnoRuntime.queryInterface( XDrawPageSupplier.class, s_aDocument); xReturn = xSuppPage.getDrawPage(); if (null == xReturn) { // the model itself is no draw page supplier - then it may be an Impress or Calc // (or any other multi-page) document XDrawPagesSupplier xSuppPages = (XDrawPagesSupplier)UnoRuntime.queryInterface( XDrawPagesSupplier.class, s_aDocument); XDrawPages xPages = xSuppPages.getDrawPages(); xReturn = (XdrawPage)UnoRuntime.queryInterface(XDrawPage.class, xPages.getByIndex(0)); // Note that this is not really error-proof code: If the document model does not support the // XDrawPagesSupplier interface, or if the pages collection returned is empty, this will break. } return xReturn; } /** retrieves the root of the hierarchy of form components */ public static XNameContainer getFormComponentTreeRoot() throws java.lang.Exception { XFormsSupplier xSuppForms = (XFormsSupplier)UnoRuntime.queryInterface( XFormsSupplier.class, getDocumentDrawPage()); XNameContainer xFormsCollection = null; if (null != xSuppForms) { xFormsCollection = xSuppForms.getForms(); } return xFormsCollection; } Form Control Models [TOPIC:com.sun.star.form.FormControlModel]The control models are discussed in these sections. The basic service for a form layer control model is [IDL:com.sun.star.form.FormControlModel] that is discussedin more detail below. A form control model promises to support the [IDL:com.sun.star.form.FormComponent] service, meaning that it can act as a child in our model hierarchy. In addition, it does not claim that the [IDL:com.sun.star.form.FormComponents] service (plural s) is supported meaning that form control models are leaves in our object tree. The only exception from this is the grid control model. It is allowed to have children representing the models of the columns. An overview of the whole model tree has been provided. With the code fragments introduced above, the following code dumps a model tree to the console: // dump the form component tree enumFormComponents(getFormComponentTreeRoot()); Control Models and Shapes [TOPIC:com.sun.star.drawing.ControlShape]There is more to know about form components in a document. From [CHAPTER:Drawing.DrawWorking.Shapes], you already know about shapes. They are also part of a document model. The control shapes, [IDL:com.sun.star.drawing.ControlShape] are made to be tied to control models. They are specialized to fully integrate form control models into a document. In theory, there can be a control shape without a model tied to it, or a control model which is part of the form component hierarchy, but not associated with any shape. In the first case, an empty shape is displayed in the document view. In the second case, you see nothing. It is possible to have a shape which is properly tied to a control model, but the control model is not part of the form component hierarchy. The model can not interact with the rest of the form layer. For example, it is unable to take advantage of its data awareness capabilities. Pay attention to the following important text section The user interface of [PRODUCTNAME] does not allow the creation of orphaned objects, but you can create them using the API. When dealing with controls through the API, ensure that there is always a valid relationship between forms, control models, and shapes. A complete object structure in a document model with respect to the components relevant for our form layer looks the following: UML diagram showing a form layer object structure in a document model Illustration 13.2 Programmatic Creation of Controls [TOPIC:com.sun.star.awt.UnoControlModel:DefaultControl]As a consequence from the previous paragraph, we now know that to insert a form control, we need to insert a control shape and control model into the document's model. The following code fragment accomplishes that: [SOURCE:Forms/FormLayer.java] /** creates a control in the document <p>Note that <em>control<em> here is an incorrect terminology. What the method really does is it creates a control shape, together with a control model, and inserts them into the document model. This will result in every view to this document creating a control described by the model-shape pair.</p> @param sFormComponentService the service name of the form component to create, e.g. "TextField" @param nXPos the abscissa of the position of the newly inserted shape @param nXPos the ordinate of the position of the newly inserted shape @param nWidth the width of the newly inserted shape @param nHeight the height of the newly inserted shape @return the property access to the control's model */ public static XPropertySet createControlAndShape(String sFormComponentService, int nXPos, int nYPos, int nWidth, int nHeight) throws java.lang.Exception { // let the document create a shape XMultiServiceFactory xDocAsFactory = (XMultiServiceFactory)UnoRuntime.queryInterface( XMultiServiceFactory.class, s_aDocument); XControlShape xShape = (XControlShape)UnoRuntime.queryInterface(XControlShape.class, xDocAsFactory.createInstance("com.sun.star.drawing.ControlShape")); // position and size of the shape xShape.setSize(new Size(nWidth * 100, nHeight * 100)); xShape.setPosition(new Point(nXPos * 100, nYPos * 100)); // and in a OOo Writer doc, the anchor can be adjusted XPropertySet xShapeProps = (XPropertySet)UnoRuntime.queryInterface(XPropertySet.class, xShape); TextContentAnchorType eAnchorType = TextContentAnchorType.AT_PAGE; if (classifyDocument(s_aDocument) == DocumentType.WRITER) { eAnchorType = TextContentAnchorType.AT_PARAGRAPH; } xShapeProps.setPropertyValue("AnchorType", eAnchorType); // create the form component (the model of a form control) String sQualifiedComponentName = "com.sun.star.form.component." + sFormComponentService; XControlModel xModel = (XControlModel)UnoRuntime.queryInterface(XControlModel.class, s_aMSF.createInstance(sQualifiedComponentName)); // knitt them xShape.setControl(xModel); // add the shape to the shapes collection of the document XShapes xDocShapes = (XShapes)UnoRuntime.queryInterface(XShapes.class, getDocumentDrawPage()); xDocShapes.add(xShape); // and outta here with the XPropertySet interface of the model XPropertySet xModelProps = (XpropertySet)UnoRuntime.queryInterface( XpropertySet.class, xModel); return xModelProps; } Looking at the example above, the basic procedure is: create and initialize a shape create a control model announce the control model to the shape insert the shape into the shapes collection of a draw page The above does not mention about inserting the control model into the form component hierarchy, which is a contradiction of our previous discussion. We have previously said that every control model must be part of this hierarchy to prevent corrupted documents, but it is not harmful. In every document, when a new control shape is inserted into the document, through the API or an interaction with a document's view, the control model is checked if it is a member of the model hierarchy. If it is not, it is automatically inserted. Moreover, if the hierarchy does not exist or is incomplete, for example, if the draw page does not have a forms collection, or this collection does not contain a form, this is also corrected automatically. With the code fragment above applied to a new document, a logical form is created automatically, inserted into the forms hierarchy, and the control model is inserted into this form. Note graphics marks a special text section Note that this is an implementation detail. Internally, there is an instance listening at the page's shapes, that reacts upon insertions. In theory, there could be other implementations of [PRODUCTNAME] API that do not contain this mechanism. In practice, the only known implementation is [PRODUCTNAME]. Pay attention to the following important text section Note that the order of operations is important. If you insert the shape into the page's shape collection, and tie it to its control model after, the document would be corrupted: Nobody would know about this new model then, and it would not be inserted properly into the form component hierarchy, unless you do this. You may have noticed that there is nothing about the view. We only created a control model. As you can see in the complete example for this chapter, when you have an open document, and insert a model and a shape, a control (the visual representation) is also created or else you would not see anything that looks like a control. The control and model have a model-view relationship. If the document window is open, this window is the document view. If the document or the model is modified by inserting a control model, the view for every open view for this document reacts appropriately and creates a control as described by the model. The [IDL:com.sun.star.awt.UnoControlModel:DefaultControl] property describes the service to be instantiated when automatically creating a control for a model. Form Components Basics According to the different form document types, there are different components in the [MODULE:com.sun.star.form] module serving different purposes. Basically, we distinguish between HTML form functionality and data awareness functionality that are covered by the form layer API. Control Models [TOPIC:com.sun.star.form.FormControlModel;com.sun.star.awt.UnoControlModel;com.sun.star.form.FormComponent;com.sun.star.beans.XPropertyState]As you know from [CHAPTER:Forms.ElementsInModel.Hierarchy.FormControlModels], the base for all our control models is the [IDL:com.sun.star.form.FormControlModel] service. Let us look at the most relevant elements of the declaration of this service and what a component must do to support it: [IDL:com.sun.star.awt.UnoControlModel] This service specifies that a form control model complies to everything required for a control model by the UNO windowing toolkit as described in module [MODULE:com.sun.star.awt]. This means support for the [IDL:com.sun.star.awt.XControlModel] interface, for property access and persistence. [IDL:com.sun.star.form.FormComponent] This service requires a form control model is part of a form component hierarchy. Refer to chapter [CHAPTER:Forms.ElementsInModel.Hierarchy]. [IDL:com.sun.star.beans.XPropertyState] This optional interface allows the control model properties to have a default value. All known implementations of the FormControlModel service support this interface. [IDL:com.sun.star.form.FormControlModel:ClassId] This property determines the class of a control model you have , and it assumes a value from the [IDL:com.sun.star.form.FormComponentType] enumeration. The same is done using the [IDL:com.sun.star.lang.XServiceInfo] interface that is supported by every component, and as shown below it can be indispensable. Using the [IDL:com.sun.star.form.FormControlModel:ClassId] property is faster. Note graphics marks a special text section Note that the [IDL:com.sun.star.form.FormControlModel] service does not state anything about data awareness. It describes the requirements for a control model which can be part of a form layer. See chapter [CHAPTER:Forms.DataAware] for additional information about the controls which are data aware. The following example shows how to determine the type of a control model using the ClassId property introduced above: [SOURCE:Forms/FLTools.java] /** retrieves the type of a form component. <p>Speaking strictly, the function recognizes more than form components. Especially, it survives a null argument. which means it can be safely applied to the a top-level forms container; and it is able to classify grid columns (which are no form components) as well.</p> */ static public String classifyFormComponentType(XPropertySet xComponent) throws com.sun.star.uno.Exception { String sType = "<unknown component>"; XServiceInfo xSI = (XserviceInfo)UnoRuntime.queryInterface(XServiceInfo.class, xComponent); XPropertySetInfo xPSI = null; if (null != xComponent) xPSI = xComponent.getPropertySetInfo(); if ( ( null != xPSI ) && xPSI.hasPropertyByName("ClassId")) { // get the ClassId property XPropertySet xCompProps = (XPropertySet)UnoRuntime.queryInterface( XPropertySet.class, xComponent); Short nClassId = (Short)xCompProps.getPropertyValue("ClassId"); switch (nClassId.intValue()) { case FormComponentType.COMMANDBUTTON: sType = "Command button"; break; case FormComponentType.RADIOBUTTON : sType = "Radio button"; break; case FormComponentType.IMAGEBUTTON : sType = "Image button"; break; case FormComponentType.CHECKBOX : sType = "Check Box"; break; case FormComponentType.LISTBOX : sType = "List Box"; break; case FormComponentType.COMBOBOX : sType = "Combo Box"; break; case FormComponentType.GROUPBOX : sType = "Group Box"; break; case FormComponentType.FIXEDTEXT : sType = "Fixed Text"; break; case FormComponentType.GRIDCONTROL : sType = "Grid Control"; break; case FormComponentType.FILECONTROL : sType = "File Control"; break; case FormComponentType.HIDDENCONTROL: sType = "Hidden Control"; break; case FormComponentType.IMAGECONTROL : sType = "Image Control"; break; case FormComponentType.DATEFIELD : sType = "Date Field"; break; case FormComponentType.TIMEFIELD : sType = "Time Field"; break; case FormComponentType.NUMERICFIELD : sType = "Numeric Field"; break; case FormComponentType.CURRENCYFIELD: sType = "Currency Field"; break; case FormComponentType.PATTERNFIELD : sType = "Pattern Field"; break; case FormComponentType.TEXTFIELD : // there are two known services with this class id: the usual text field, // and the formatted field sType = "Text Field"; if (( null != xSI) && xSI.supportsService( "com.sun.star.form.component.FormattedField")) { sType = "Formatted Field"; } break; default: break; } } else { if ((null != xSI) && xSI.supportsService("com.sun.star.form.component.DataForm")) { sType = "Form"; } } return sType; } Note the special handling for the value [IDL:com.sun.star.form.FormComponentType:TEXTFIELD]. There are two different services where a component implementing them is required to act as text field, the [IDL:com.sun.star.form.component.TextField] and [IDL:com.sun.star.form.component.FormattedField]. Both services describe a text component, thus both have a class id of [IDL:com.sun.star.form.FormComponentType:TEXTFIELD]. To distinguish between them, ask the components for more details using the [IDL:com.sun.star.lang.XServiceInfo] interface. Forms [TOPIC:com.sun.star.form.component.Form;com.sun.star.form.component.HTMLForm;com.sun.star.form.XForm;com.sun.star.awt.XTabControllerModel]The [PRODUCTNAME] API features different kinds of forms, namely the [IDL:com.sun.star.form.component.Form], [IDL:com.sun.star.form.component.HTMLForm], and [IDL:com.sun.star.form.component.DataForm]. The two different aspects described with these services are HTML forms used in HTML documents, and data aware forms used to access databases. Data awareness is discussed thoroughly in [CHAPTER:Forms.DataAware]. Note graphics marks a special text section Though different services exist for HTML and data aware forms, there is only one form implementation in [PRODUCTNAME] htat implements both services simultaneously. The common denominator of HTML forms and data aware forms is described in the [IDL:com.sun.star.form.component.Form] service. It includes the FormComponent and FormComponents service, in addition to the following elements: [IDL:com.sun.star.form.XForm] This interface identifies the component as a form that can be done with other methods, such as the [IDL:com.sun.star.lang.XServiceInfo] interface. The [IDL:com.sun.star.form.XForm] interface distinguishes a form component as a form. The XForm interface inherits from [IDL:com.sun.star.form.XFormComponent] to indicate the difference, and does not add any further operations. [IDL:com.sun.star.awt.XTabControllerModel] This is used for controlling tab ordering and control grouping. As a logical form is a container for control models, it is a natural place to administer information about the relationship of its control children. The tab order, that is, the order in which the focus travels through the controls associated with the control models when the user presses the Tab key, is a relationship, and thus is maintained on the form. Note that changing the tab order through this interface also affects the models. The [IDL:com.sun.star.form.FormControlModel] service has an optional property TabIndexthat contains the relative position of the control in the tabbing order. For example, a straightforward implementation of [IDL:com.sun.star.awt.XTabControllerModel:setControlModels]() would be simply to adjust all the TabIndex properties of the models passed to this method. HTML Forms [TOPIC:com.sun.star.form.component.HTMLForm]The [IDL:com.sun.star.form.component.HTMLForm] service reflects the requirements for HTML form documents. Looking at HTML specifications, you can submit forms using different encodings and submit methods, and reset forms. The HTMLForm service description reflects this by supporting the interfaces [IDL:com.sun.star.form.XReset] and [IDL:com.sun.star.form.XSubmit], as well as some additional properties related to the submit functionality. The semantics of these interfaces and properties are straightforward.For additional details, refer to the service description, as well as the HTML specification. Data Awareness A major feature of forms in [PRODUCTNAME] is that they can be data aware. You create form documents where the user manipulates data from a database that is accessible in [PRODUCTNAME]. For more details about data sources, refer to chapter [CHAPTER:Database]. This includes data from any table of a database, or data from a query based on one or more tables. The basic idea is that a logical form cis associated with a database result set. A form control model, which is a child of that form, is bound to a field of this result set, exchanging the data entered by the user with the result set field. Forms Forms as Row Sets [TOPIC:com.sun.star.sdb.RowSet]Besides forms, there is already a component that supports a result set, the [IDL:com.sun.star.sdb.RowSet].If you look at the [IDL:com.sun.star.form.component.DataForm], a DataForm also implements the [IDL:com.sun.star.sdb.RowSet] service, and extends it with additional functionality. Row sets are described in [CHAPTER:Database.Manipulate.RowSet]. Loadable Forms [TOPIC:com.sun.star.form.XLoadable;com.sun.star.form.XLoadListener]A major difference of data forms compared to the underlying row set is the that forms are loaded, and t provide an interface to manipulate this state. XLoadable xLoad = (XLoadable)FLTools.getParent(aControlModel, XLoadable.class); xLoad.reload(); Loading is the same as executing the underlying row set, that is, invoking the [IDL:com.sun.star.sdbc.XRowSet:execute]() method. The [IDL:com.sun.star.form.XLoadable] is designed to fit the needs of a form document, for example, it a unloads an already loaded form. The example above shows how to reload a form. Reloading is executing the row set again. Using reload instead of execute has the advantage of advanced listener mechanisms: Look at the [IDL:com.sun.star.form.XLoadable] interface. You can add a [IDL:com.sun.star.form.XLoadListener]. This listener not only tells you when load-related events have occurred that is achieved by the [IDL:com.sun.star.sdbc.XRowSetListener], but also when they are about to happen. In a complex scenario where different listeners are added to different aspects of a form, you use the [IDL:com.sun.star.form.XLoadable:reloading]() call to disable all other listeners temporarily. Re-executing a row set is a complex process, thus it triggers a lot of events that are only an after effect of the re-execution. Note graphics marks a special text section Though all the functionality provided by [IDL:com.sun.star.form.XLoadable] can be simulated using the [IDL:com.sun.star.sdbc.XRowSet] interface, you should always use the former. Due to the above-mentioned, more sophisticated listener mechanisms, implementations have a chance to do loading, reloading and unloading much smoother then. An additional difference between loading and executing is the positioning of the row set: When using [IDL:com.sun.star.sdbc.XRowSet:execute](), the set is positioned before the first record. When you use [IDL:com.sun.star.form.XLoadable:load](), the set is positioned on the first record, as you would expect from a form. Sub Forms A powerful feature of [PRODUCTNAME] are sub forms. This does not mean that complete form documents are embedded into other form documents, instead sub form relationships are realized by nesting logical forms in the form component hierarchy. When a form notices that its parent is not the forms container when it is loaded and in live mode, but is dependent on another form, it no longer acts as a top-level form. Whenever the parent or master form moves to another record, the content of the sub or detail form is re-fetched. This way, the content of the sub form is made dependent on the actual value of one or more fields of the parent form. Typical use for a relationship are tables that are linked through key columns, usually in a 1:n relationship. You use a master form to travel trough all records of the table on the 1 side of the relationship, and a detail form that shows the records of the table on the n side of the relationship where the foreign key matches the primary key of the master table. To create nested forms at runtime, use the following example: [SOURCE:Forms/FormLayer.java] // retrieve or create the master form m_xMasterForm = .... // bind it to the salesman table m_xMasterForm.setPropertyValue("DataSourceName", m_aParameters.sDataSourceName); m_xMasterForm.setPropertyValue("CommandType", new Integer(CommandType.TABLE)); m_xMasterForm.setPropertyValue("Command", "SALESMAN"); // create the details form XIndexContainer xSalesForm = m_aDocument.createSubForm(m_xMasterForm, "Sales"); XPropertySet xSalesFormProps = (XPropertySet)UnoRuntime.queryInterface( XPropertySet.class, xSalesForm); // bind it to the all those sales belonging to a variable salesmen xSalesFormProps.setPropertyValue("DataSourceName", m_aParameters.sDataSourceName); xSalesFormProps.setPropertyValue("CommandType", new Integer( CommandType.COMMAND)); xSalesFormProps.setPropertyValue("Command", "SELECT * FROM SALES AS SALES WHERE SALES.SNR = :salesman"); // the master-details connection String[] aMasterFields = new String[] {"SNR"}; // the field in the master form String[] aDetailFields = new String[] {"salesman"}; // the name in the detail form xSalesFormProps.setPropertyValue("MasterFields", aMasterFields); xSalesFormProps.setPropertyValue("DetailFields", aDetailFields); The code snippet works on the following table structure: Relation between the two tables SALESMEN and SALES Illustration 13.3 The code is straight forward, except setting up the connection between the two forms. The master form is bound to SALESMEN, and the detail form is bound to a statement that selects all fields from SALES, filtered for records where the foreign key, SALES.SNR, equals a parameter named salesman. As soon as the MasterFields and DetailFields properties are set, the two forms are connected. Every time the cursor in the master form moves, the detail form reloads after filling the salesman parameter with the actual value of the master forms SNR column. Filtering and Sorting Forms support quick and easy filtering and sorting like the underlying row sets. For this, the properties [IDL:com.sun.star.sdb.RowSet:Filter], [IDL:com.sun.star.sdb.RowSet:ApplyFilter] and [IDL:com.sun.star.sdb.RowSet:Order] area used. [SOURCE:Forms/SalesFilter.java] // set this as filter on the form String sCompleteFilter = ""; if ((null != sOdbcDate) && (0 != sOdbcDate.length())) { sCompleteFilter = "SALEDATE >= "; sCompleteFilter += sOdbcDate; } m_xSalesForm.setPropertyValue("Filter", sCompleteFilter); m_xSalesForm.setPropertyValue("ApplyFilter", new Boolean(true)); // and reload the form XLoadable xLoad = (XLoadable)UnoRuntime.queryInterface(XLoadable.class, m_xSalesForm); xLoad.reload(); In this fragment, a filter string is built first. The "SALEDATE >= {D '2002-12-02'}" is an example for a filter string. In general, everything that appears after the WHERE clause of an SQL statement is set as a Filter property value. The same holds true for the Order property value and an ORDER BY clause. Tip graphics marks a hint section in the text Note the notation for the date in braces: This is the standard ODBC notation for date values, and it is the safest method to supply [PRODUCTNAME] with date values. It also works if you are using non-ODBC data sources, as long as you do not switch on the Native SQL option. Refer to[IDL:com.sun.star.sdbc.Statement:EscapeProcessing]. [PRODUCTNAME] understands and sometimes returns other notations, for instance, in the user interface where that makes sense, but these are locale-dependent, which means you have to know the current locale if you use them. Then the ApplyFilter property is set to true. This is for safety, because the value of this property is unknown when creating a new form. Everytime you have a form or row set, and you want to change the filter, remember to set the ApplyFilter property at least once. Afterwards, reload() is called. In general, ApplyFilter allows the user of a row set to enable or disable the current filter quickly without remembering it. To see what the effects of the current filter are, set ApplyFilter to false and reload the form. Parameters [TOPIC:com.sun.star.form.XDatabaseParameterBroadcaster;com.sun.star.form.XDatabaseParameterListener]Data Aware Forms are based on statements. As with other topics in this chapter, this is not form specific, instead it is a functionality inherited from the underlying [IDL:com.sun.star.sdb.RowSet]. Statements contain parameters where some values are not specified, and are not dependent on actual values in the underlying tables. Instead they have to be filled each time the row set is executed, that is, the form is loaded or reloaded. A typical example for a statement containing a parameter is SELECT * FROM SALES WHERE SALES.SNR = :salesman There is a named parameter salesman, which is filled before a row set based on a statement is executed. The orthodox method to use is the [IDL:com.sun.star.sdbc.XParameters] interface, exported by the row set. However, forms allow another way. They export the [IDL:com.sun.star.form.XDatabaseParameterBroadcaster] interface that allows your component to add itself as a listener for an event which is triggered whenever the form needs parameter values. In a form, filling parameters is a three-step procedure. Consider a form that needs three parameters for execution. The master-detail relationship is evaluated. If the form's parent is a [IDL:com.sun.star.form.component.DataForm], then the MasterFields and DetailFields properties are evaluated to fill in parameter values. For an example of how this relationship is evaluated, refer to chapter [CHAPTER:Forms.DataAware.Forms.Subforms]. If there are parameter values left, that is, not filled in, the calls to the [IDL:com.sun.star.sdbc.XParameters] interface are examined. All values previously set through this interface are filled in. If there are still parameter values left, the [IDL:com.sun.star.form.XDatabaseParameterListener]s are invoked. Any component can add itself as a listener using the [IDL:com.sun.star.form.XDatabaseParameterBroadcaster] interface implemented by the form.The listeners then have the chance to fill in anything still missing. [BUG641+]Unfortunately, [PRODUCTNAME] Basic scripts currently cannot follow the last step of this procedure—there is a known implementation issue which prevents this. Data Aware Controls [TOPIC:com.sun.star.form.DataAwareControlModel]The second part of the Data Awareness capabilities of [PRODUCTNAME] are data aware controls. While a form is always associated with a complete result set, it represents this result set, a single control is bound to one data column that is part of the form which is the control's parent. As always, the relevant information is stored in the control model. The basic service for control models which are data-aware is [IDL:com.sun.star.form.DataAwareControlModel]. There are two connections between a control model and the column it is bound to: DataField This is the property that determines the name of the field to bind to. Upon loading the form, a control model searches the data columns of the form for this name, and connects to it. An explanation for "connects" is provided below.Note that this property isa suggestion only. It tells the control model to connect to the data column, but this connection may fail for various reasons, for example, no such column may exist in the row set.Even if this property is set to a non-empty string, this does not mean anything about the control being connected. BoundField Once a control model has connected itself to a data column, the respective column object is also remembered. This saves clients of a control model the effort to examine and handle the DataField , they simply rely on BoundField.Opposite to the DataField property, BoundField is reliable in that it is a valid column object if and only if the control is properly connected. The overall relationship for data awareness is as follows: UML diagram showing the com.sun.star.form.DataAwareControlModel service Illustration 13.4 Control Models as Bound Components You expect that the control displays the current data of the column it is tied to. Current data means the data in the row that the [IDL:com.sun.star.form.component.DataForm] is currently located on. Now, the control does not know about data-awareness, only the control model does, but we already have a connection between the model and control: As described in the chapter about model-view interaction, [CHAPTER:Forms.MVC.Interaction], the control listens for changes to the model properties, as well as updates them when a user interacts with the control directly. For instance, you know the Text property of a simple text input field, [IDL:com.sun.star.form.component.TextField]that is updated by the control when the user enters text. When the property is updated through any other means, the control reacts appropriately and adjusts the text it displays. This mechanism is found in all controls. The only difference is the property used to determine the contents to be displayed. For instance, numeric controls [IDL:com.sun.star.form.component.NumericField] have a property Value representing the current numerical value to be displayed.Although the name differs, all control models have a dedicated content property. This is where the data-awareness comes in. A data-aware control model bound to a data column uses its content property to exchange data with this column. As soon as the column value changes, the model forwards the new value to its content property, and notifies its listeners. One of these listeners is the control that updates its display: Sequence diagram showing a listener notification in the context of a data from Illustration 13.5 Committing Controls [TOPIC:com.sun.star.form.XBoundComponent]The second direction of the data transfer is back from what the user enters into the control. The text entered by a user is immediately forwarded to the value property of the control model. This way, both the control and the control model are always consistent. Next, the content property is transferred into the data column the control is bound to. As opposed to the first step, this is not done automatically. Instead, this control is committed actively. Committing is the process of transferring the current value of the control to the database column. The interface used for this is [IDL:com.sun.star.form.XBoundComponent] that provides the method commit. Note that the XBoundComponent is derived from [IDL:com.sun.star.form.XUpdateBroadcaster]. This means that listeners are added to a component to monitor and veto the committing of data. The following diagram shows what happens when the user decides to save the current record after changing a control: Sequence diagram of saving a from after changing a control Illustration 13.6 Note that in the diagram, there is a controller instance involved. In general, this is any instance capable of controlling the user-form interaction. In [PRODUCTNAME], for every document view and form, there is an instance of the [IDL:com.sun.star.form.FormController] service, together with some not-yet UNO-based code that takes on the role of a controller. Scripting and Events To create form documents which are able to do more than just reading and writing plain data, it's often necessary to enhance them with scripting functionality. That is, for a given form component, you want to declare that a certain script should be called, when a certain event occurs. For instance, you may need a check box control, which calls a certain macro when its check state changes. Within this macro, you can for instance enable or disable other controls, which depend on the box being checked. Of course you can do this completely programmatically: Just bind an script to the OnLoad event of the whole document, therein, create an [IDLS:com.sun.star.awt.XItemListener], and register it at the checkbox control in question, using the [IDLS:com.sun.star.awt.XCheckBox] interface. Well, this is ugly, isn't it? You don't want the whole scripting engine to start up upon loading the document. You want it to do so at the latest possible point, which is the moment the user clicks the check box for the first time. Form components feature a mechanism to save you this. For every form component part of a hierarchy of form components, you can specify a script to be called upon a certain event. You only specify this once, at the time the form component is created and placed in the document, and never need to care for it, again. Look at the [IDL:com.sun.star.form.FormComponents] service which we already encountered earlier. It includes the interface [IDL:com.sun.star.script.XEventAttacherManager], and this is the key here. It allows you to manage the events which are associated with the elements in the [IDLS:com.sun.star.form.FormComponents] container. Note that an event together with an associated script is described by the [IDLS:com.sun.star.script.ScriptEventDescriptor], with the following elements: Properties of [IDL:com.sun.star.script.ScriptEventDescriptor] [IDLS:com.sun.star.script.ScriptEventDescriptor:ListenerType] is the completely qualified name of a listener interface. This implies that you can only register scripts for events which are notified using the usual UNO means. [IDLS:com.sun.star.script.ScriptEventDescriptor:EventMethod] specifies a method of the ListenerType interface. Together with the ListenerType member, this completely describes the event which a script is to be registered for. [IDLS:com.sun.star.script.ScriptEventDescriptor:AddListenerParam] specifies a parameter which is to be used when adding listeners (as described by the ListenerType element). For most listener types, this is not necessary. [IDLS:com.sun.star.script.ScriptEventDescriptor:ScriptType] specifies which type of script is to be associated with the event. [IDL:com.sun.star.form.FormComponents] currently only support StarBasic here. [IDLS:com.sun.star.script.ScriptEventDescriptor:ScriptCode] specifies which script code to call. In case of ScriptType being StarBasic, this must specify a Basic procedure or function, either within the application-wide code repository, or within the document which the form component belongs to. In the first case, the script code starts with "application:", else with "document:". The following example registers a certain Basic procedure for the event which is triggered when the state of a radio button control changes (e.g. it is being selected or deselected). Dim oEvent as new com.sun.star.script.ScriptEventDescriptor oEvent.ListenerType = "com.sun.star.awt.XItemListener" oEvent.EventMethod = "itemStateChanged" oEvent.ScriptType = "StarBasic" oEvent.ScriptCode = "application:Standard.macro_assignment.onColorChange" oSampleForm.registerScriptEvent( i, oEvent ) For the ith sub component of oSampleForm, this associates the macro onColorChange, located in the module macro_assignment of the application-wide library Standard, with the [IDLS:com.sun.star.awt.XItemListener:itemStateChanged] event. You could use this with every form component which supports notification of [IDLS:com.sun.star.awt.XItemListener]s, in particular with radio buttons ([IDLS:com.sun.star.awt.XRadioButton]) and check boxes ([IDLS:com.sun.star.awt.XCheckBox]). Note that simply registering script events at a [IDLs:com.sun.star.form.FormComponents] instance doesn't do anything. In particular, it does not yet mean that the script will be called automatically. In fact, the [IDLS:com.sun.star.script.XEventAttacherManager] interface merely acts as a container to remember the associated events. In a living form document, there are controller instances involved, which care for the scripts really being called, basically by adding themselves as [IDLS:com.sun.star.script.XScriptListener] to the event attacher manager. Common Tasks This chapter is dedicated to problems that may arise when you are working with (or script) form documents, and cannot be solved by [PRODUCTNAME]'s built-in methods, but have a solution in the [PRODUCTNAME] UNO API. Initializing Bound Controls All form controls specify a default value that is used when initially displaying the control, and when it is reset. For instance, resetting ([IDL:com.sun.star.form.XReset]) happens when a form is moved to the insert row, that allows data to be inserted as a new row into the underlying row set. Now, you do not want a fixed default value for new records, but a dynamically generated one that is dependent on the actual context at the moment the new record is entered. [BUG641+]Or, you want to have real null values for date fields. This is currently not possible, because the [IDL:com.sun.star.form.component.DateField] service interprets a null default as an instruction to use the current system date. Effectively, you cannot have date fields in forms which default to null on new records, but you can get this by programming the API. [SOURCE:Forms/FormLayer.java] public void handleReset(EventObject aEvent) throws com.sun.star.uno.RuntimeException { if (((Boolean)xFormProps.getPropertyValue("IsNew")).booleanValue()) { // the form is positioned on the insert row Object aModifiedFlag = xFormProps.getPropertyValue("IsModified"); // get the columns of the form XColumnsSupplier xSuppCols = (XColumnsSupplier)UnoRuntime.queryInterface( XColumnsSupplier.class, xFormProps); XNameAccess xCols = xSuppCols.getColumns(); // and update the date column with a NULL value XColumnUpdate xDateColumn = (XColumnUpdate)UnoRuntime.queryInterface( XColumnUpdate.class, xCols.getByName("SALEDATE")); xDateColumn.updateNull(); // then restore the flag xFormProps.setPropertyValue("IsModified", aModifiedFlag); } } The first decision is where to step in. We chose to add a reset-listener to the form, so that the form is reset as soon as it has been positioned on the new record. The [IDL:com.sun.star.form.XReset:resetted]() method is called after the positioning is done. However, resets also occur for various reasons therefore check if the form is really positioned on the insert row, indicated by the IsNew property being true. Now besides retrieving and updating the data column with the desired value, null, there is another obstacle. When the form is moved to the insert row, and some values are initialized, the row should not be modified. This is because a modified row is saved in the database, and we only initialized the new row with the defaults, the user did not enter data., We do not want to store the row, therefore we save and restore the IsModified flag on the form while doing the update. Automatic Key Generation [TOPIC:com.sun.star.sdb.XRowSetApproveBroadcaster]Another problem frequently encountered is the automatic generation of unique keys. There are reasons for doing this on the client side, and missing support, for example, auto-increment fields in your database backend, or you need this value before inserting the row. [PRODUCTNAME] is currently limited in re-fetching the server-side generated value after a record has been inserted. Assume that you have a method called generateUniqueKey() to generate a unique key that could be queried from a key generator on a database server, or in a single-user-environment by selecting the maximum of the existing keys and incrementing it by 1. This fragment inserts the generated value into the given column of a given form: [SOURCE:Forms/KeyGenerator.java] public void insertUniqueKey(XPropertySet xForm, String sFieldName) throws com.sun.star.uno.Exception { // get the column object XColumnsSupplier xSuppCols = (XColumnsSupplier)UnoRuntime.queryInterface( XColumnsSupplier.class, xForm); XNameAccess xCols = xSuppCols.getColumns(); XColumnUpdate xCol = (XColumnUpdate)UnoRuntime.queryInterface( XColumnUpdate.class, xCols.getByName(sFieldName)); xCol.updateInt(generateUniqueKey(xForm, sFieldName)); } A solution to determine when the insertion is to happen has been introduced in a previous chapter, that is, we could fill in the value as soon as the form is positioned on the insert row, wait for the user's input in the other fields, and save the record. Another approach is to step in immediately before the record is inserted. For this, the [IDL:com.sun.star.sdb.XRowSetApproveBroadcaster] is used. It notifies listeners when rows are inserted, the listeners can veto this, and final changes can be made to the new record: [SOURCE:Forms/KeyGenerator.java] public boolean approveRowChange(RowChangeEvent aEvent) throws com.sun.star.uno.RuntimeException { if (RowChangeAction.INSERT == aEvent.Action) { // the affected form XPropertySet xFormProps = (XpropertySet)UnoRuntime.queryInterface( XpropertySet.class, aEvent.Source); // insert a new unique value insertUniqueKey(xFormProps, m_sFieldName); } return true; } Data Validation [PRODUCTNAME]'s only offering for client-side data validation is that it automatically rejects null values for fields where input is required. Often you want to validate data as soon as it is written. You have two possibilities here: From the chapter [CHAPTER:Forms.DataAware.Controls.Commit], you know that you can approve updates, and veto the changes a control wants to write into the data column it is bound to. Additionally, you can step in later. You know how to use a [IDL:com.sun.star.sdb.XRowSetApproveListener] for doing last-minute changes to a record that is about to be inserted.Additionally, you can use the listener to approve changes to the row set data. As When the [IDL:com.sun.star.sdb.RowChangeAction] is sent to the listeners, it distinguishes between different kinds of data modification. You can implement listeners that act differently for insertions and simple updates. Note the important differences between both solutions. Using an [IDL:com.sun.star.form.XUpdateListener] implies that the data operations are vetoed for a given control. Your listener is invoked as soon as the respective control is committed, for instance, when it loses the focus. This implies that changes done to the data column by other means than through this control are not monitored. The second alternative is using an [IDL:com.sun.star.sdb.XRowSetApproveListener] meaning you veto changes immediately before they are sent to the database. Thus, it is irrelevant where they have been made previously. In addition, error messages that are raised when the user actively tries to save the record are considered less disturbing than error messages raised when the user simply leaves a control. The example below shows the handling for denying empty values for a given control: [SOURCE:Forms/GridFieldValidator.java] public boolean approveUpdate(EventObject aEvent) throws com.sun.star.uno.RuntimeException { boolean bApproved = true; // the control model which fired the event XPropertySet xSourceProps = UNO.queryPropertySet(aEvent.Source); String sNewText = (String)xSourceProps.getPropertyValue("Text"); if (0 == sNewText.length()) { // say that the value is invalid showInvalidValueMessage(); bApproved = false; // reset the control value // for this, we take the current value from the row set field the control // is bound to, and forward it to the control model XColumn xBoundColumn = UNO.queryColumn(xSourceProps.getPropertyValue("BoundField")); if (null != xBoundColumn) { xSourceProps.setPropertyValue("Text", xBoundColumn.getString()); } } return bApproved; } Programmatic Assignment of Scripts to Events Sometimes, you want to programmatically create a document, including form controls, and assign certain scripts to certain events for those controls. In the user interface, this is straight-forward by using the property browser. Programmatically, this is somewhat more difficult. As an example, let's say you want to programmatically create a document, containing radio buttons. When those radio buttons change their state (i.e. are selected), a certain StarBasic script should be called. One possibility would be to make use of the OnLoad event of the document as a whole. Therein, you could create a listener of the desired type (in our sample case: [IDL:com.sun.star.awt.XItemListener]), and register it at the control in question. In StarBasic, you can create an UNO listener using the procedure CreateUnoListener. This approach bears three disadvantages: First, it's expensive. The scripting environment would be loaded every time the document is loaded, which, honestly, is too early. We want it to be loaded as late as possible, which is when the radio buttons really change their state. Second, it's error prone. There are certain circumstances where StarBasic listeners are automatically revoked, without the document being closed. In those cases, you would need to manually reload the document, or re-run your OnLoad script, for re-creating the listener. Third, it's complex. You would, in your OnLoad initialization script, need to manually obtain the control in questions, which can be quite some lines of code. It's much more elegant to use the event attacher manager mechanism described in [CHAPTER:Forms.ScriptingAndEvents], which makes the solution to this pretty straight-forward. The following example creates a text document with 3 radio buttons, all 3 calling the same script when being selected. Side note: When you try it out, you may need to adjust the location of the script being called: At the moment, it assumes a module name macro_assignment in the application-wide library named Standard. REM ***** BASIC ***** Option Explicit Sub Main ' create a new writer document Dim oDocument as Object Dim oEmptyArgs() as new com.sun.star.beans.PropertyValue oDocument = StarDesktop.LoadComponentFromURL( "private:factory/swriter", "_blank", 0, oEmptyArgs ) Erase oEmptyArgs ' create a new logical form Dim oFormsCollection as Object oFormsCollection = oDocument.DrawPage.Forms Dim oSampleForm as Object oSampleForm = createUnoService( "com.sun.star.form.component.DataForm" ) oFormsCollection.insertByName( "sample form", oSampleForm ) ' create three radio buttons associated with three colors Dim oControlShape as Object Dim oControlModel as Object ' we want to add the equivalent of an com.sun.star.awt.XItemListener Dim sListenerInterfaceName as String sListenerInterfaceName = "com.sun.star.awt.XItemListener" Dim sListenerMethodName as String sListenerMethodName = "itemStateChanged" ' we want the onColorChange function in this module to be called Dim sMacroLocation as String sMacroLocation = "application:Standard.macro_assignment.onColorChange" ' note that this assumes that the module is called macro_assignment, and ' resides in the "Standard" library of the application-wide Basic macros Dim sColors(2) as String sColors(0) = "red" sColors(1) = "green" sColors(2) = "blue" Dim i as Integer For i = 0 To 2 ' a shape oControlShape = oDocument.createInstance( "com.sun.star.drawing.ControlShape" ) positionShape( oControlShape, 1000, 1000 + i * 800, 5000, 600 ) ' a control model oControlModel = createUnoService( "com.sun.star.form.component.RadioButton" ) oControlModel.Name = "colors" oControlModel.Label = "make it " & UCase( sColors( i ) ) oControlModel.Tag = sColors( i ) oSampleForm.insertByIndex( i, oControlModel ) ' knit both oControlShape.Control = oControlModel ' yes, unfortunately the terminology is inconsistent here ... ' add the shape to the DrawPage oDocument.DrawPage.add( oControlShape ) ' bind a macro to the "stateChanged" event Dim oEvent as new com.sun.star.script.ScriptEventDescriptor oEvent.ListenerType = sListenerInterfaceName oEvent.EventMethod = sListenerMethodName oEvent.ScriptType = "StarBasic" oEvent.ScriptCode = sMacroLocation oSampleForm.registerScriptEvent( i, oEvent ) Next i ' switch the document (view) to alive mode Dim oURL as new com.sun.star.util.URL oURL.Complete = ".uno:SwitchControlDesignMode" createUnoService( "com.sun.star.util.URLTransformer" ).parseStrict( oURL ) Dim oDispatcher as Object oDispatcher = oDocument.CurrentController.Frame.queryDispatch( oURL, "", 63 ) oDispatcher.dispatch( oURL, oEmptyArgs() ) Erase oURL ' set the focus to the first control oDocument.CurrentController.getControl( oSampleForm.getByIndex( 0 ) ).setFocus End Sub ' this sets the size and position of a given shape ' Additionally, it anchors this shape at a paragraph Sub positionShape( oShape as Object, X as Integer, Y as Integer, Width as Integer, Height as Integer ) oShape.AnchorType = com.sun.star.text.TextContentAnchorType.AT_PARAGRAPH ' Not that this implies that you can use it for text documents only. ' The rest of the function also works for shapes in other documents Dim oPos as new com.sun.star.awt.Point oPos.X = X oPos.Y = Y oShape.setPosition( oPos ) Erase oPos Dim oSize as new com.sun.star.awt.Size oSize.Width = Width oSize.Height = Height oShape.setSize( oSize ) Erase oSize End Sub ' This will be bound to the radio button's state changes ' At the moment, it simply changes their text color, but of course ' you could do more than this here ... Sub onColorChange( oClickEvent as Object ) If ( oClickEvent.Selected > 0 ) Then Dim nColor as Long Select Case oClickEvent.Source.Model.Tag Case "red" nColor = CLng( "&HFF0000" ) case "green" nColor = CLng( "&H00FF00" ) case "blue" nColor = CLng( "&H0000FF" ) End Select Dim oControlParent as Object oControlParent = oClickEvent.Source.Model.Parent Dim i as Integer For i = 0 to oControlParent.getCount() - 1 oControlParent.getByIndex( i ).TextColor = nColor Next i End If End Sub