StarOffice 7 (Win32) Forms Frank Schönheit 2002-01-25T09:18:48 2004-08-23T16:23:17 en-US 652 P4DT22H50M24S 0 0 22809 22756 true false false false view2 10167 10904 0 0 22807 22754 0 100 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 14 Product name and Release number Manual Title  March 2001 Chapter 14 14 Product name and Release number Manual Title  March 2001 14 14 Product name and Release number Manual Title  March 2001 14 14 14 14 Product name and Release number Manual Title 14 Product name and Release number Manual Title Contents 14 Contents 14 14 Product name and Release number Manual Title  March 2001 Index 14 Chapter 13 14 14 Index 14 Chapter 13 Forms 14 14 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]. Since [PRODUCTNAME] [OO2.0], form controls also feature a generalization of this concept. They can be bound to external components, which supply an own value. Both values – the one of the external component, and the current value of the control – are synchronized, so that a change in one of them is immediately propagated to the other. This allows new features, where the most notable is that you can bind form controls to spreadsheet cells. When discussing forms, the difference between form documents and logical forms have to be distinguished. The form document refers to a document as a whole, and while logical forms are 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 , since it 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 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 straigh 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. External value suppliers Chapter [CHAPTER:Forms.DataAware.Controls] talked about form controls which exchange their value, as entered by the user, with database columns. At certain times, such a form control initializes itself from the column, or writes its current value into it. In addition, list and combo box controls are able to retrieve, in various different ways, their list content from a database. Since [PRODUCTNAME] [OO2.0], there's an additional possibility for form controls to exchange data with external components. This is kind of a generalization of the data awareness concept: Form controls are now able to bind their value to any external value supplier, without knowing anything about it except an abstract UNO interface. Similar, list and combo boxes can obtain their list content from an external component, as long as they support a certain interface. The module [MODULE:com.sun.star.form.binding] collects all interfaces and services related to this new functionality. Value Bindings [TOPIC:com.sun.star.form.binding.XBindableValue][TOPIC:com.sun.star.form.binding.XValueBinding]Unlike the functionality for binding form controls to database columns, value bindings are external to the form control/model. A control which can be bound (note that not all existing controls actually can) supports a certain interface, and a binding supports another one – that's all both parties need to know. Illustration 13.7 shows the most important interfaces and services collaborating here. Illustration 13.7 Basic class diagram for value components and value bindings Note that there's no notion about form controls at all, yet. Those interfaces are only about components representing a value, and components implementing a binding for this value. In fact, the generic mechanism for binding values is described with complete disregard of form controls. We'll call components supporting the [IDLS:com.sun.star.form.binding.XBindableValue] interface value components. The central interface is [IDLS:com.sun.star.form.binding.XValueBinding], which is to be supported by components which want to impose their value on a value component. Let's have a look at its methods. Methods of [IDL:com.sun.star.form.binding.XValueBinding] [IDLS:com.sun.star.form.binding.XValueBinding:getSupportedValueTypes]() allows negotiation of a type in which values should be exchanged. Usually, both a binding and a value component only support a certain set of types, in which the values can be exchanged. If the sets of a given binding and a given value component to not intersect, both can not be knit together. [IDLS:com.sun.star.form.binding.XValueBinding:supportsType]() This allows a value component to explicitly ask whether a given binding supports a given type. This method can be used as shortcut: components don't need to examine the complete type sequence of a binding. Additionally, this method is usually used to implement a precedence of types. A value component can ask a potential binding for certain supported types, in a certain order. The first type which is accepted by the binding (if any) can then be used for exchanging the value. [IDLS:com.sun.star.form.binding.XValueBinding:getValue]() retrieves the current value as represented by the binding. Callers specify a type of the value, and the binding must provide the value in exactly this type, or throw an [IDL:com.sun.star.form.binding.IncompatibleTypesException] if this is not possible. [IDLS:com.sun.star.form.binding.XValueBinding:setValue]() propagates a new value to the binding The [IDLS:com.sun.star.form.binding.ValueBinding] service extends the [IDLS:com.sun.star.form.binding.XValueBinding] interface with two aspects: [IDL:com.sun.star.util.XModifyBroadcaster] This allows a value binding to actively notify changes in its value. A value component can register itself as [IDLS:com.sun.star.util.XModifyListener] at the binding. In fact, that's the only possibility how the relationship between the two parties – the binding and the value component – can become bidirectional. Without support of the [IDLS:com.sun.star.util.XModifyBroadcaster] interface, value components can only actively propagate their value to the binding, but not vice versa. support for read-only bindings The property [IDLS:com.sun.star.form.binding.ValueBinding:ReadOnly] can be used to specify that the value represented by a binding currently cannot be modified. If the read-only state of a binding's value can change during its lifetime, it should allow registering [IDLS:com.sun.star.beans.XPropertyChangeListener]s for the [IDLS:com.sun.star.form.binding.ValueBinding:ReadOnly] property, so value components using the binding can act on the current state. Form Controls accepting Value Bindings Now how do form controls and value bindings relate to each other? When looking at all the form control functionality we know so far, the following questions come to mind and need to be answered: Which control types do support value bindings? For a given control type, which aspect actually is its value, which is exchanged with an external binding? How do external value bindings interact with data awareness, i.e. controls which exchange their value with a database column? What the heck can you do with all this? The first two questions are easy: Every control which allows user input also supports value bindings. The data which the user entered (this may be, for instance, plain text, or an image, or a check state) is considered the value of the control, and thus exchanged with the external binding.This definition of a control value should not be really new – actually, it's what you'd expect when you know data-aware controls. So the basic service is the [IDL:com.sun.star.form.binding.BindableControlModel], which specifies a control model supporting external value bindings. For a concrete control type – say: a check box –, you could expect a service like BindableCheckBox now, which would specify how a check box control model exchanges its value with an external binding. However, in real life – well, in [PRODUCTNAME] [OO2.0] –, all controls which potentially could support a binding also are data aware (see [CHAPTER:Forms.DataAware.Controls]). Thus, the first step is to answer the third question from above. The service [IDL:com.sun.star.form.binding.BindableDataAwareControlModel] is about data aware control models with value binding capabilities. You are referred to the documentation of the [IDLS:com.sun.star.form.binding.BindableDataAwareControlModel] service for all the details, but the two most interesting should be mentioned here: Priority External value bindings overrule any active SQL-column binding. If an external component is bound to a control model which currently has an active SQL binding, this SQL binding is suspended, until the external binding is revoked. Immediacy When a [IDLS:com.sun.star.form.binding.BindableDataAwareControlModel] is bound to an external value, then every change in the control model's value is immediately reflected in the external binding. This is a difference to SQL bindings of most [IDLS:com.sun.star.form.DataAwareControlModel]s, where changes in the model's value are only propagated to the bound column upon explicit request via [IDLS:com.sun.star.form.XBoundComponent:commit]. Have a look at Illustration 13.2, it shows the service hierarchy for control models which are also value components. It also shows how concrete control types fit in there, by exemplarily using check boxes. Illustration 13.8form control models supporting value bindings Well, what's open is the last question from the above list: What is this good for? [PRODUCTNAME] [OO2.0] already contains two practical applications: Spreadsheet cell bindings In a [PRODUCTNAME] spreadsheet document, you always could insert form controls. Since version [OO2.0], you can, in their properties, bind them to arbitrary cells within the document. That is, every change done in this cell is propagated to the control, and vice versa. This is implemented using the value binding mechanism described in this chapter – see [IDL:com.sun.star.table.CellValueBinding] for more details. The following piece of code creates such a cell value binding in a spreadsheet document, for cell A1 on the first sheet, and knits it to a numeric control model: // insert our sample control XPropertySet numericControl = m_formLayer.insertControlLine( "DatabaseFormattedField", "enter a value", "", 10 ); // a value binding for cell A1 on the first CellAddress address = new CellAddress( (short)0, (short)0, (short)0 ); Object[] initParam = new Object[] { new NamedValue( "BoundCell", address ) }; XValueBinding cellBinding = (XValueBinding)UnoRuntime.queryInterface( XValueBinding.class, m_document.createInstanceWithArguments( "com.sun.star.table.CellValueBinding", initParam ) ); // bind it to the control model XBindableValue bindable = (XBindableValue)UnoRuntime.queryInterface( XBindableValue.class, numericControl ); bindable.setValueBinding( cellBinding ); XML form bindings [PRODUCTNAME] [OO2.0] features XML forms. These are form documents whose data model is a DOM tree. They are realized with the usual form controls and logical forms, and this time the controls (speaking strictly: their models, of course) are bound to DOM nodes using the value binding mechanism. External List Sources [TOPIC:com.sun.star.form.binding.XListEntrySource][TOPIC:com.sun.star.form.binding.XListEntrySink]The previous chapter introduced an abstraction of the data aware mechanism for form controls: They can not only exchange their value with database columns, but also with arbitrary value bindings, without knowing anything about them except some UNO interfaces. Now, when you look at what controls can do with database content, you stumble upon list and combo boxes: They are able to retrieve the content of their lists from a database (in various flavors, such as table fields or table content, but this doesn't matter here). Similar to the value binding mechanism, there's also an abstraction available for components supplying list entries to form controls: [IDL:com.sun.star.form.binding.ListEntrySource] and [IDL:com.sun.star.form.binding.XListEntrySink]. The relationship between [IDLS:com.sun.star.form.binding.XListEntrySource]s and [IDLS:com.sun.star.form.binding.XListEntrySink]s is shown in Illustration 13.3. Illustration 13.9 Interfaces and services involved with external list sources As with value bindings, [PRODUCTNAME] already makes use of this concept in spreadsheet documents. The following piece of code, for instance, creates a [IDLS:com.sun.star.table.CellRangeListSource], and binds it to a list box. After that, the list boxes content will always be synchronized with the content in the chosen cell range. CellRangeAddress rangeAddress = new CellRangeAddress( sheet, column, topRow, column, bottomRow ); Object[] initParam = new Object[] { new NamedValue( "CellRange", rangeAddress ) }; XListEntrySource entrySource = (XListEntrySource)UnoRuntime.queryInterface( XListEntrySource.class, m_document.createInstanceWithArguments( "com.sun.star.table.CellRangeListSource", initParam ) ); XListEntrySink consumer = (XListEntrySink)UnoRuntime.queryInterface( XListEntrySink.class, listBox ); consumer.setListEntrySource( entrySource ); Note that a [IDLS:com.sun.star.table.CellRangeListSource] can not be obtained at a global service manager. Instead, you have to retrieve it from the document to whose cells you want to bind the list box. Validation [TOPIC:com.sun.star.form.validation.XValidator][TOPIC:com.sun.star.form.validation.XValidatable]Form controls in [PRODUCTNAME] always featured some simple kind of validation for their value. For instance, for a numeric field, you can specify minimum an maximum values ([IDLS:com.sun.star.awt.UnoControlNumericFieldModel:ValueMin] and [IDLS:com.sun.star.awt.UnoControlNumericFieldModel:ValueMax]). However, those validity constraints have some disadvantages: They're enforced as soon as the control loses the focus. That is, if you enter a number into a numeric field, which is greater than the allowed maximum, then it will be automatically corrected to be the maximum. They're enforced silently. There's no warning to the user, and no visual feedback at the moment the value is invalid, and not yet corrected. In particular, there's no explanation why a certain input was (or will be) automatically corrected. [PRODUCTNAMT] [OO2.0] features an new mechanism for validating the content of form controls, at the time the user inputs it. The basic interface for this is [IDLS:com.sun.star.form.validation.XValidator]: Methods of [IDL:com.sun.star.form.validation.XValidator] [IDLS:com.sun.star.form.validation.XValidator:isValid] This is the central method of a validator. It is able to decide, for a given value, whether it's valid or not. Note that there's no notion about the semantics of valid - this is in the responsibility of the concrete service implementing this interface. [IDLS:com.sun.star.form.validation.XValidator:explainInvalid] explains, for a given value, why it is considered invalid. The explanation should be human-readable – other components are expected to present it to the user. [IDLS:com.sun.star.form.validation.XValidator:addValidityConstraintListener] registers a new validity listener. As a basic idea, a validator may not be stateless: Depending on its current internal state, it may consider the very same value as valid or invalid. Such validator components should notify a change in their state, and thus a potential change in the validity of associated [IDLS:com.sun.star.form.validation.XValidatable] instances, to all listeners registered with this method. The listeners are encouraged to re-validate whatever data is guarded by the validator. [IDLS:com.sun.star.form.validation.XValidator:removeValidityConstraintListener] revokes a previously registered validity listener. A validator is to be used with a component which can be validated: [IDLS:com.sun.star.form.validation.XValidatable]. This interface is pretty straight-forward: It allows to set and to get a validator instance. 'till here, nothing is said about form components, yet. You may also note that nothing is said about the data which is being validated. Though a value is passed to the [IDLS:com.sun.star.form.validation.XValidator:isValid] method, it's not explained where it originates from. In particular, the [IDLS:com.sun.star.form.validation.XValidatable] does not specify a means to obtain its value. This is where [IDL:com.sun.star.form.validation.XValidatableFormComponent] comes in. Note that it derives from [IDLS:com.sun.star.form.validation.XValidatable]. Methods of [IDL:com.sun.star.form.validation.XValidatableFormComponent] [IDLS:com.sun.star.form.validation.XValidatableFormComponent:isValid] determines whether the current value, as represented by the component, is valid. This is a shortcut to calling the validator's [IDLS:com.sun.star.form.validation.XValidator:isValid] method, with the current value of the component. [IDLS:com.sun.star.form.validation.XValidatableFormComponent:getCurrentValue] specifies the current value, as represented by the component. As an example, a [IDLS:com.sun.star.form.component.TextField] would return its text here, while a [IDLS:com.sun.star.form.component.DateField] would return its date value. [IDLS:com.sun.star.form.validation.XValidatableFormComponent:addFormComponentValidityListener] adds a listener to observe the validity of the component. This validity is determined by two aspects: The current value of the component, and the validator's opinion about this value (and thus implicitly by the current validator of the component, which may also change). To be notified of changes in this composed validity, you need to register a [IDLS:com.sun.star.form.validation.XFormComponentValidityListener] at the form component. [IDLS:com.sun.star.form.validation.XValidatableFormComponent:removeFormComponentValidityListener] revokes a previously registered validity listener. Now, the overall picture for services and interfaces concerned with form control validation looks as follows: Illustration 13.10Validation of form controls Note the [IDLS:com.sun.star.form.validation.ValidatableControlModel] service: It specifies the basic functionality of form control models which allow validating their current value against an external validator. In [PRODUCTNAME], there's one feature which uses the functionality introduced in this chapter: XML form documents. They're implemented using the interfaces and services from [MODULE:com.sun.star.xforms]. In particular, an XForms binding ([IDLS:com.sun.star.xforms.Binding]) is a validator. This way, [PRODUCTNAME] form controls can be used to enter values for XForms DOM trees, respecting the restrictions imposed on those value as part of the XForms model. Validation in [PRODUCTNAME] The [PRODUCTNAME] Software Development Kit shows the power of form control validation with an example program. This program creates a form document with various types of controls, most of which are bound to external validators. Those validator objects impose (exemplary and rather arbitrary) restrictions on the control values. As shown in the program, the form runtime environment of [PRODUCTNAME] makes use of several features of the validation API, illustrating the advantage over the old, property-based built-in validation mentioned in chapter [CHAPTER:Validation]. For instance, invalid values in form controls (where invalid is defined by the external validator object) are not enforcing a valid value automatically. Instead, the invalidity is shown with a red border around the control (If the control does not allow for a red border, its text is underlined with a red waves). Additionally, the explanation why a certain value is invalid appears as tool tip at the respective control. This way, the user who fills in a form has an immediate feedback about which values and controls need her attention, without destroying whatever information she already entered. Validations and Bindings Chapter [CHAPTER:ValueBindings] introduced form components which can exchange the value with external components, as long as those support the [IDLS:com.sun.star.form.binding.XValueBinding] interface. Also, chapter [CHAPTER:Validation] introduced form components whose value can be validated by external components. Now wouldn't it make sense to combine those concepts? This way, it would be possible to build highly customized form documents. In fact, this is what the [IDL:com.sun.star.form.validation.ValidatableBindableControlModel] service does: it combines the services [IDLS:com.sun.star.form.binding.BindableControlModel] with the [IDLS:com.sun.star.form.validation.ValidatableControlModel]. The interesting contract here is that as soon as you establish a validator at the model ([IDLS:com.sun.star.form.validation.XValidatable:setValidator]), which is also an [IDLS:com.sun.star.form.binding.XBindableValue], then it is used both as value binding and as validator. Every attempt to establish another binding will be denied, as long as the combined validator/binding is in place. Note that in [PRODUCTNAME], every form control model which is both bindable and validatable also supports the [IDLS:com.sun.star.form.validation.ValidatableBindableControlModel] service. That is, the validator and the binding are coupled, if possible. Common Tasks This chapter is dedicated to problems that may arise when you are working with (or scripting) 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 If you happen to have a scripting language which is not capable of creating own components (like StarBasic), then the validation mechanisms described in chapter [CHAPTER:Validation] are of no use: They rely on you creating a component which implements the [IDLS:com.sun.star.form.validation.XValidator] interface. If you, despite this, want to validate data in controls bound to a database, then you have two alternative possibilities:[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 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.AdditionallyBesides this, you can use the listener to approve changes to the row set data. As 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; }