svn commit: r550168 - in /ofbiz/trunk/framework/widget: dtd/ src/org/ofbiz/widget/fo/ src/org/ofbiz/widget/form/ src/org/ofbiz/widget/html/ src/org/ofbiz/widget/xml/

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
1 message Options
Reply | Threaded
Open this post in threaded view
|

svn commit: r550168 - in /ofbiz/trunk/framework/widget: dtd/ src/org/ofbiz/widget/fo/ src/org/ofbiz/widget/form/ src/org/ofbiz/widget/html/ src/org/ofbiz/widget/xml/

jacopoc
Author: jacopoc
Date: Sat Jun 23 22:09:41 2007
New Revision: 550168

URL: http://svn.apache.org/viewvc?view=rev&rev=550168
Log:
Implemented support for the 'position' attribute in list based form widgets.

1) added new attribute,  'default-position', to the auto-fields-entity and auto-fields-service element: this is useful to specify a default position for the fields inherited by an entity or service definition
2) change the signature of two methods in the FormStringRendered interface (and classes that implement it), to pass the number of columns spanned (in header and item rows)
3) a lot of code has been refactored/modified in the ModelForm class: new support for position in list, code cleanups with no functional changes, bug fixes (including bugs reported in OFBIZ-1071 and OFBIZ-542), added comments to code, formatting fixes


Modified:
    ofbiz/trunk/framework/widget/dtd/widget-form.xsd
    ofbiz/trunk/framework/widget/src/org/ofbiz/widget/fo/FoFormRenderer.java
    ofbiz/trunk/framework/widget/src/org/ofbiz/widget/form/FormStringRenderer.java
    ofbiz/trunk/framework/widget/src/org/ofbiz/widget/form/ModelForm.java
    ofbiz/trunk/framework/widget/src/org/ofbiz/widget/form/ModelFormField.java
    ofbiz/trunk/framework/widget/src/org/ofbiz/widget/html/HtmlFormRenderer.java
    ofbiz/trunk/framework/widget/src/org/ofbiz/widget/xml/XmlFormRenderer.java

Modified: ofbiz/trunk/framework/widget/dtd/widget-form.xsd
URL: http://svn.apache.org/viewvc/ofbiz/trunk/framework/widget/dtd/widget-form.xsd?view=diff&rev=550168&r1=550167&r2=550168
==============================================================================
--- ofbiz/trunk/framework/widget/dtd/widget-form.xsd (original)
+++ ofbiz/trunk/framework/widget/dtd/widget-form.xsd Sat Jun 23 22:09:41 2007
@@ -227,6 +227,7 @@
                 </xs:restriction>
             </xs:simpleType>
         </xs:attribute>
+        <xs:attribute type="xs:positiveInteger" name="default-position" default="1"/>
     </xs:attributeGroup>
     <xs:element name="auto-fields-entity">
         <xs:complexType>
@@ -246,6 +247,7 @@
                 </xs:restriction>
             </xs:simpleType>
         </xs:attribute>
+        <xs:attribute type="xs:positiveInteger" name="default-position" default="1"/>
     </xs:attributeGroup>
     <xs:element name="sort-order">
         <xs:complexType>

Modified: ofbiz/trunk/framework/widget/src/org/ofbiz/widget/fo/FoFormRenderer.java
URL: http://svn.apache.org/viewvc/ofbiz/trunk/framework/widget/src/org/ofbiz/widget/fo/FoFormRenderer.java?view=diff&rev=550168&r1=550167&r2=550168
==============================================================================
--- ofbiz/trunk/framework/widget/src/org/ofbiz/widget/fo/FoFormRenderer.java (original)
+++ ofbiz/trunk/framework/widget/src/org/ofbiz/widget/fo/FoFormRenderer.java Sat Jun 23 22:09:41 2007
@@ -222,8 +222,15 @@
         this.appendWhitespace(buffer);
     }
 
-    public void renderFormatHeaderRowCellOpen(StringBuffer buffer, Map context, ModelForm modelForm, ModelFormField modelFormField) {
-        buffer.append("<fo:table-cell font-weight=\"bold\" text-align=\"center\" border=\"solid black\" padding=\"2pt\">");
+    public void renderFormatHeaderRowCellOpen(StringBuffer buffer, Map context, ModelForm modelForm, ModelFormField modelFormField, int positionSpan) {
+        buffer.append("<fo:table-cell ");
+        if (positionSpan > 1) {
+            buffer.append("number-columns-spanned=\"");
+            buffer.append(positionSpan);
+            buffer.append("\" ");
+        }
+        buffer.append("font-weight=\"bold\" text-align=\"center\" border=\"solid black\" padding=\"2pt\"");
+        buffer.append(">");
         buffer.append("<fo:block>");
         this.appendWhitespace(buffer);
     }
@@ -257,8 +264,13 @@
         this.appendWhitespace(buffer);
     }
 
-    public void renderFormatItemRowCellOpen(StringBuffer buffer, Map context, ModelForm modelForm, ModelFormField modelFormField) {
+    public void renderFormatItemRowCellOpen(StringBuffer buffer, Map context, ModelForm modelForm, ModelFormField modelFormField, int positionSpan) {
         buffer.append("<fo:table-cell ");
+        if (positionSpan > 1) {
+            buffer.append("number-columns-spanned=\"");
+            buffer.append(positionSpan);
+            buffer.append("\" ");
+        }
         String areaStyle = modelFormField.getWidgetAreaStyle();
         if (UtilValidate.isEmpty(areaStyle)) {
             areaStyle = "tabletext";

Modified: ofbiz/trunk/framework/widget/src/org/ofbiz/widget/form/FormStringRenderer.java
URL: http://svn.apache.org/viewvc/ofbiz/trunk/framework/widget/src/org/ofbiz/widget/form/FormStringRenderer.java?view=diff&rev=550168&r1=550167&r2=550168
==============================================================================
--- ofbiz/trunk/framework/widget/src/org/ofbiz/widget/form/FormStringRenderer.java (original)
+++ ofbiz/trunk/framework/widget/src/org/ofbiz/widget/form/FormStringRenderer.java Sat Jun 23 22:09:41 2007
@@ -54,7 +54,7 @@
 
     public void renderFormatHeaderRowOpen(StringBuffer buffer, Map context, ModelForm modelForm);
     public void renderFormatHeaderRowClose(StringBuffer buffer, Map context, ModelForm modelForm);
-    public void renderFormatHeaderRowCellOpen(StringBuffer buffer, Map context, ModelForm modelForm, ModelFormField modelFormField);
+    public void renderFormatHeaderRowCellOpen(StringBuffer buffer, Map context, ModelForm modelForm, ModelFormField modelFormField, int positionSpan);
     public void renderFormatHeaderRowCellClose(StringBuffer buffer, Map context, ModelForm modelForm, ModelFormField modelFormField);
 
     public void renderFormatHeaderRowFormCellOpen(StringBuffer buffer, Map context, ModelForm modelForm);
@@ -63,7 +63,7 @@
     
     public void renderFormatItemRowOpen(StringBuffer buffer, Map context, ModelForm modelForm);
     public void renderFormatItemRowClose(StringBuffer buffer, Map context, ModelForm modelForm);
-    public void renderFormatItemRowCellOpen(StringBuffer buffer, Map context, ModelForm modelForm, ModelFormField modelFormField);
+    public void renderFormatItemRowCellOpen(StringBuffer buffer, Map context, ModelForm modelForm, ModelFormField modelFormField, int positionSpan);
     public void renderFormatItemRowCellClose(StringBuffer buffer, Map context, ModelForm modelForm, ModelFormField modelFormField);
     public void renderFormatItemRowFormCellOpen(StringBuffer buffer, Map context, ModelForm modelForm);
     public void renderFormatItemRowFormCellClose(StringBuffer buffer, Map context, ModelForm modelForm);

Modified: ofbiz/trunk/framework/widget/src/org/ofbiz/widget/form/ModelForm.java
URL: http://svn.apache.org/viewvc/ofbiz/trunk/framework/widget/src/org/ofbiz/widget/form/ModelForm.java?view=diff&rev=550168&r1=550167&r2=550168
==============================================================================
--- ofbiz/trunk/framework/widget/src/org/ofbiz/widget/form/ModelForm.java (original)
+++ ofbiz/trunk/framework/widget/src/org/ofbiz/widget/form/ModelForm.java Sat Jun 23 22:09:41 2007
@@ -24,17 +24,21 @@
 import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
+import java.util.TreeMap;
+import java.util.Collection;
 import java.util.NoSuchElementException;
 import java.util.Set;
 import java.util.TreeSet;
 
 import javolution.util.FastList;
+import javolution.util.FastMap;
 
 import bsh.EvalError;
 import bsh.Interpreter;
 
 import org.ofbiz.base.util.BshUtil;
 import org.ofbiz.base.util.Debug;
+import org.ofbiz.base.util.UtilMisc;
 import org.ofbiz.base.util.UtilValidate;
 import org.ofbiz.base.util.UtilXml;
 import org.ofbiz.base.util.collections.FlexibleMapAccessor;
@@ -590,7 +594,7 @@
                         ModelField modelField = modelEntity.getField(modelParam.fieldName);
                         if (modelField != null) {
                             // okay, populate using the entity field info...
-                            ModelFormField modelFormField = this.addFieldFromEntityField(modelEntity, modelField, autoFieldsService.defaultFieldType);
+                            ModelFormField modelFormField = this.addFieldFromEntityField(modelEntity, modelField, autoFieldsService.defaultFieldType, autoFieldsService.defaultPosition);
                             if (UtilValidate.isNotEmpty(autoFieldsService.mapName)) {
                                 modelFormField.setMapName(autoFieldsService.mapName);
                             }
@@ -601,7 +605,7 @@
                     }
                 }
 
-                ModelFormField modelFormField = this.addFieldFromServiceParam(modelService, modelParam, autoFieldsService.defaultFieldType);
+                ModelFormField modelFormField = this.addFieldFromServiceParam(modelService, modelParam, autoFieldsService.defaultFieldType, autoFieldsService.defaultPosition);
                 if (UtilValidate.isNotEmpty(autoFieldsService.mapName)) {
                     modelFormField.setMapName(autoFieldsService.mapName);
                 }
@@ -609,7 +613,7 @@
         }
     }
 
-    public ModelFormField addFieldFromServiceParam(ModelService modelService, ModelParam modelParam, String defaultFieldType) {
+    public ModelFormField addFieldFromServiceParam(ModelService modelService, ModelParam modelParam, String defaultFieldType, int defaultPosition) {
         // create field def from service param def
         ModelFormField newFormField = new ModelFormField(this);
         newFormField.setName(modelParam.name);
@@ -617,6 +621,7 @@
         newFormField.setAttributeName(modelParam.name);
         newFormField.setTitle(modelParam.formLabel);
         newFormField.induceFieldInfoFromServiceParam(modelService, modelParam, defaultFieldType);
+        newFormField.setPosition(defaultPosition);
         return this.addUpdateField(newFormField);
     }
 
@@ -635,20 +640,21 @@
                 // don't ever auto-add these, should only be added if explicitly referenced
                 continue;
             }
-            ModelFormField modelFormField = this.addFieldFromEntityField(modelEntity, modelField, autoFieldsEntity.defaultFieldType);
+            ModelFormField modelFormField = this.addFieldFromEntityField(modelEntity, modelField, autoFieldsEntity.defaultFieldType, autoFieldsEntity.defaultPosition);
             if (UtilValidate.isNotEmpty(autoFieldsEntity.mapName)) {
                 modelFormField.setMapName(autoFieldsEntity.mapName);
             }
         }
     }
 
-    public ModelFormField addFieldFromEntityField(ModelEntity modelEntity, ModelField modelField, String defaultFieldType) {
+    public ModelFormField addFieldFromEntityField(ModelEntity modelEntity, ModelField modelField, String defaultFieldType, int defaultPosition) {
         // create field def from entity field def
         ModelFormField newFormField = new ModelFormField(this);
         newFormField.setName(modelField.getName());
         newFormField.setEntityName(modelEntity.getEntityName());
         newFormField.setFieldName(modelField.getName());
         newFormField.induceFieldInfoFromEntityField(modelEntity, modelField, defaultFieldType);
+        newFormField.setPosition(defaultPosition);
         return this.addUpdateField(newFormField);
     }
 
@@ -737,7 +743,8 @@
         if (!skipStart) formStringRenderer.renderFormOpen(buffer, context, this);
 
         // render all hidden & ignored fields
-        this.renderHiddenIgnoredFields(buffer, context, formStringRenderer, alreadyRendered);
+        List hiddenIgnoredFieldList = this.getHiddenIgnoredFields(context, alreadyRendered, tempFieldList, -1);
+        this.renderHiddenIgnoredFields(buffer, context, formStringRenderer, hiddenIgnoredFieldList);
 
         // render formatting wrapper open
         // This should be covered by fieldGroup.renderStartString
@@ -946,13 +953,14 @@
         // render formatting wrapper open
         formStringRenderer.renderFormatListWrapperOpen(buffer, context, this);
 
+        int numOfColumns = 0;
         // ===== render header row =====
         if (!getHideHeader()) {
-            this.renderHeaderRow(buffer, context, formStringRenderer);
+            numOfColumns = this.renderHeaderRow(buffer, context, formStringRenderer);
         }
 
         // ===== render the item rows =====
-        this.renderItemRows(buffer, context, formStringRenderer, true);
+        this.renderItemRows(buffer, context, formStringRenderer, true, numOfColumns);
 
         // render formatting wrapper close
         formStringRenderer.renderFormatListWrapperClose(buffer, context, this);
@@ -964,140 +972,206 @@
         // render formatting wrapper open
         formStringRenderer.renderFormatListWrapperOpen(buffer, context, this);
 
+        int numOfColumns = 0;
         // ===== render header row =====
-        this.renderHeaderRow(buffer, context, formStringRenderer);
+        numOfColumns = this.renderHeaderRow(buffer, context, formStringRenderer);
 
         // ===== render the item rows =====
-        this.renderItemRows(buffer, context, formStringRenderer, false);
+        this.renderItemRows(buffer, context, formStringRenderer, false, numOfColumns);
 
         formStringRenderer.renderFormatListWrapperClose(buffer, context, this);
         
         formStringRenderer.renderMultiFormClose(buffer, context, this);
     }
 
-    public void renderHeaderRow(StringBuffer buffer, Map context, FormStringRenderer formStringRenderer) {
-        formStringRenderer.renderFormatHeaderRowOpen(buffer, context, this);
+    public int renderHeaderRow(StringBuffer buffer, Map context, FormStringRenderer formStringRenderer) {
+        int maxNumOfColumns = 0;
 
-        // render title for each field, except hidden & ignored, etc
-
-        // start by rendering all display and hyperlink fields, until we
-        //get to a field that should go into the form cell, then render
-        //the form cell with all non-display and non-hyperlink fields, then
-        //do a start after the first form input field and
-        //render all display and hyperlink fields after the form
-
-        // do the first part of display and hyperlink fields
-        Iterator displayHyperlinkFieldIter = this.fieldList.iterator();
-        ModelFormField previousModelFormField = null;
-        while (displayHyperlinkFieldIter.hasNext()) {
-            ModelFormField modelFormField = (ModelFormField) displayHyperlinkFieldIter.next();
-            ModelFormField.FieldInfo fieldInfo = modelFormField.getFieldInfo();
-
-            // don't do any header for hidden or ignored fields
-            if (fieldInfo.getFieldType() == ModelFormField.FieldInfo.HIDDEN || fieldInfo.getFieldType() == ModelFormField.FieldInfo.IGNORED) {
-                continue;
-            }
-
-            //Modification Nicolas to support Two or more field with the same name and they are used with condition
-            if (previousModelFormField != null && previousModelFormField.getTitle(context).equals(modelFormField.getTitle(context)) &&
-                    !(previousModelFormField.isUseWhenEmpty() && modelFormField.isUseWhenEmpty())) {
-                continue;
-            }
-
-            if (fieldInfo.getFieldType() != ModelFormField.FieldInfo.DISPLAY && fieldInfo.getFieldType() != ModelFormField.FieldInfo.DISPLAY_ENTITY && fieldInfo.getFieldType() != ModelFormField.FieldInfo.HYPERLINK) {
-                // okay, now do the form cell
-                break;
+        // We will render one title/column for all the fields with the same name
+        // in this model: we can have more fields with the same name when use-when
+        // conditions are used or when a form is extended or when the fields are
+        // automatically retrieved by a service or entity definition.
+        List tempFieldList = FastList.newInstance();
+        tempFieldList.addAll(this.fieldList);
+        for (int j = 0; j < tempFieldList.size(); j++) {
+            ModelFormField modelFormField = (ModelFormField) tempFieldList.get(j);
+            for (int i = j+1; i < tempFieldList.size(); i++) {
+                ModelFormField curField = (ModelFormField) tempFieldList.get(i);
+                if (curField.getName() != null && curField.getName().equals(modelFormField.getName())) {
+                    tempFieldList.remove(i--);
+                }
             }
-
-            // DON'T check this for the header row, doesn't really make sense, should always show the header: if (!modelFormField.shouldUse(context)) { continue; }
-
-            formStringRenderer.renderFormatHeaderRowCellOpen(buffer, context, this, modelFormField);
-
-            formStringRenderer.renderFieldTitle(buffer, context, modelFormField);
-
-            formStringRenderer.renderFormatHeaderRowCellClose(buffer, context, this, modelFormField);
-      
-            //Modification Nicolas
-            previousModelFormField = modelFormField;
         }
 
-        List headerFormFields = new LinkedList();
-        Iterator formFieldIter = this.fieldList.iterator();
-        //boolean isFirstFormHeader = true;
-        while (formFieldIter.hasNext()) {
-            ModelFormField modelFormField = (ModelFormField) formFieldIter.next();
-            ModelFormField.FieldInfo fieldInfo = modelFormField.getFieldInfo();
-
-            // don't do any header for hidden or ignored fields
-            if (fieldInfo.getFieldType() == ModelFormField.FieldInfo.HIDDEN || fieldInfo.getFieldType() == ModelFormField.FieldInfo.IGNORED) {
-                continue;
-            }
+        // ===========================
+        // Preprocessing
+        // ===========================
+        // We get a sorted (by position, ascending) set of lists;
+        // each list contains all the fields with that position.
+        Collection fieldListsByPosition = this.getFieldListsByPosition(tempFieldList);
+        Iterator fieldListsByPositionIter = fieldListsByPosition.iterator();
+        List fieldRowsByPosition = FastList.newInstance(); // this list will contain maps, each one containing the list of fields for a position
+        while (fieldListsByPositionIter.hasNext()) {
+            int numOfColumns = 0;
+            List mainFieldList = (List) fieldListsByPositionIter.next();
+
+            List innerDisplayHyperlinkFieldsBegin = FastList.newInstance();
+            List innerFormFields = FastList.newInstance();
+            List innerDisplayHyperlinkFieldsEnd = FastList.newInstance();
+
+            // render title for each field, except hidden & ignored, etc
+
+            // start by rendering all display and hyperlink fields, until we
+            //get to a field that should go into the form cell, then render
+            //the form cell with all non-display and non-hyperlink fields, then
+            //do a start after the first form input field and
+            //render all display and hyperlink fields after the form
+
+            // prepare the two lists of display and hyperlink fields
+            // the fields in the first list will be rendered as columns before the
+            // combined column for the input fields; the fields in the second list
+            // will be rendered as columns after it
+            Iterator displayHyperlinkFieldIter = mainFieldList.iterator();
+            boolean inputFieldFound = false;
+            while (displayHyperlinkFieldIter.hasNext()) {
+                ModelFormField modelFormField = (ModelFormField) displayHyperlinkFieldIter.next();
+                ModelFormField.FieldInfo fieldInfo = modelFormField.getFieldInfo();
+
+                // if the field's title is explicitly set to "" (title="") then
+                // the header is not created for it; this is useful for position list
+                // where one line can be rendered with more than one row, and we
+                // only want to display the title header for the main row
+                String modelFormFieldTitle = modelFormField.getTitle(context);
+                if ("".equals(modelFormFieldTitle)) {
+                    // TODO: for now this is commented: there are too many form definitions
+                    //       that needs to be fixed.
+                    //continue;
+                }
+                // don't do any header for hidden or ignored fields
+                if (fieldInfo.getFieldType() == ModelFormField.FieldInfo.HIDDEN || fieldInfo.getFieldType() == ModelFormField.FieldInfo.IGNORED) {
+                    continue;
+                }
 
-            // skip all of the display/hyperlink fields
-            if (fieldInfo.getFieldType() == ModelFormField.FieldInfo.DISPLAY || fieldInfo.getFieldType() == ModelFormField.FieldInfo.DISPLAY_ENTITY || fieldInfo.getFieldType() == ModelFormField.FieldInfo.HYPERLINK) {
-                continue;
-            }
+                if (fieldInfo.getFieldType() != ModelFormField.FieldInfo.DISPLAY && fieldInfo.getFieldType() != ModelFormField.FieldInfo.DISPLAY_ENTITY && fieldInfo.getFieldType() != ModelFormField.FieldInfo.HYPERLINK) {
+                    inputFieldFound = true;
+                    continue;
+                }
 
-            if (!modelFormField.shouldUse(context)) {
-                continue;
+                // separate into two lists the display/hyperlink fields found before and after the first input fields
+                if (!inputFieldFound) {
+                    innerDisplayHyperlinkFieldsBegin.add(modelFormField);
+                } else {
+                    innerDisplayHyperlinkFieldsEnd.add(modelFormField);
+                }
+                numOfColumns++;
             }
 
-            headerFormFields.add(modelFormField);
-        }
-
-        // render the "form" cell
-        formStringRenderer.renderFormatHeaderRowFormCellOpen(buffer, context, this);
+            // prepare the combined title for the column that will contain the form/input fields
+            Iterator formFieldIter = mainFieldList.iterator();
+            while (formFieldIter.hasNext()) {
+                ModelFormField modelFormField = (ModelFormField) formFieldIter.next();
+                ModelFormField.FieldInfo fieldInfo = modelFormField.getFieldInfo();
 
-        Iterator headerFormFieldIter = headerFormFields.iterator();
-        while (headerFormFieldIter.hasNext()) {
-            ModelFormField modelFormField = (ModelFormField) headerFormFieldIter.next();
-            //ModelFormField.FieldInfo fieldInfo = modelFormField.getFieldInfo();
-
-            if (separateColumns || modelFormField.getSeparateColumn())
-                formStringRenderer.renderFormatItemRowCellOpen(buffer, context, this, modelFormField);
-
-            // render title (unless this is a submit or a reset field)
-            formStringRenderer.renderFieldTitle(buffer, context, modelFormField);
+                // don't do any header for hidden or ignored fields
+                if (fieldInfo.getFieldType() == ModelFormField.FieldInfo.HIDDEN || fieldInfo.getFieldType() == ModelFormField.FieldInfo.IGNORED) {
+                    continue;
+                }
 
-            if (separateColumns || modelFormField.getSeparateColumn())
-                formStringRenderer.renderFormatItemRowCellClose(buffer, context, this, modelFormField);
+                // skip all of the display/hyperlink fields
+                if (fieldInfo.getFieldType() == ModelFormField.FieldInfo.DISPLAY || fieldInfo.getFieldType() == ModelFormField.FieldInfo.DISPLAY_ENTITY || fieldInfo.getFieldType() == ModelFormField.FieldInfo.HYPERLINK) {
+                    continue;
+                }
 
-            if (headerFormFieldIter.hasNext()) {
-                // TODO: determine somehow if this is the last one... how?
-               if (!separateColumns && !modelFormField.getSeparateColumn())
-                    formStringRenderer.renderFormatHeaderRowFormCellTitleSeparator(buffer, context, this, modelFormField, false);
+                innerFormFields.add(modelFormField);
             }
-        }
-
-        formStringRenderer.renderFormatHeaderRowFormCellClose(buffer, context, this);
-
-        // render the rest of the display/hyperlink fields
-        while (displayHyperlinkFieldIter.hasNext()) {
-            ModelFormField modelFormField = (ModelFormField) displayHyperlinkFieldIter.next();
-            ModelFormField.FieldInfo fieldInfo = modelFormField.getFieldInfo();
-
-            // don't do any header for hidden or ignored fields
-            if (fieldInfo.getFieldType() == ModelFormField.FieldInfo.HIDDEN || fieldInfo.getFieldType() == ModelFormField.FieldInfo.IGNORED) {
-                continue;
+            if (innerFormFields.size() > 0) {
+                numOfColumns++;
             }
 
-            // skip all non-display and non-hyperlink fields
-            if (fieldInfo.getFieldType() != ModelFormField.FieldInfo.DISPLAY && fieldInfo.getFieldType() != ModelFormField.FieldInfo.DISPLAY_ENTITY && fieldInfo.getFieldType() != ModelFormField.FieldInfo.HYPERLINK) {
-                continue;
+            if (maxNumOfColumns < numOfColumns) {
+                maxNumOfColumns = numOfColumns;
             }
+            
+            fieldRowsByPosition.add(UtilMisc.toMap("displayBefore", innerDisplayHyperlinkFieldsBegin,
+                                                   "inputFields", innerFormFields,
+                                                   "displayAfter", innerDisplayHyperlinkFieldsEnd));
+        }
+        // ===========================
+        // Rendering
+        // ===========================
+        Iterator fieldRowsByPositionIt = fieldRowsByPosition.iterator();
+        while (fieldRowsByPositionIt.hasNext()) {
+            Map listsMap = (Map) fieldRowsByPositionIt.next();
+            List innerDisplayHyperlinkFieldsBegin = (List)listsMap.get("displayBefore");
+            List innerFormFields = (List)listsMap.get("inputFields");
+            List innerDisplayHyperlinkFieldsEnd = (List)listsMap.get("displayAfter");
+            
+            int numOfCells = innerDisplayHyperlinkFieldsBegin.size() +
+                             innerDisplayHyperlinkFieldsEnd.size() +
+                             (innerFormFields.size() > 0? 1: 0);
+            int numOfColumnsToSpan = maxNumOfColumns - numOfCells + 1;
+            if (numOfColumnsToSpan < 1) {
+                numOfColumnsToSpan = 1;
+            }
+
+            if (numOfCells > 0) {
+                formStringRenderer.renderFormatHeaderRowOpen(buffer, context, this);
+                Iterator innerDisplayHyperlinkFieldsBeginIt = innerDisplayHyperlinkFieldsBegin.iterator();
+                while (innerDisplayHyperlinkFieldsBeginIt.hasNext()) {
+                    ModelFormField modelFormField = (ModelFormField) innerDisplayHyperlinkFieldsBeginIt.next();
+                    // span columns only if this is the last column in the row (not just in this first list)
+                    if (innerDisplayHyperlinkFieldsBeginIt.hasNext() || numOfCells > innerDisplayHyperlinkFieldsBegin.size()) {
+                        formStringRenderer.renderFormatHeaderRowCellOpen(buffer, context, this, modelFormField, 1);
+                    } else {
+                        formStringRenderer.renderFormatHeaderRowCellOpen(buffer, context, this, modelFormField, numOfColumnsToSpan);
+                    }
+                    formStringRenderer.renderFieldTitle(buffer, context, modelFormField);
+                    formStringRenderer.renderFormatHeaderRowCellClose(buffer, context, this, modelFormField);
+                }
+                if (innerFormFields.size() > 0) {
+                    // TODO: manage colspan
+                    formStringRenderer.renderFormatHeaderRowFormCellOpen(buffer, context, this);
+                    Iterator innerFormFieldsIt = innerFormFields.iterator();
+                    while (innerFormFieldsIt.hasNext()) {
+                        ModelFormField modelFormField = (ModelFormField) innerFormFieldsIt.next();
 
-            if (!modelFormField.shouldUse(context)) {
-                continue;
-            }
+                        if (separateColumns || modelFormField.getSeparateColumn()) {
+                            formStringRenderer.renderFormatItemRowCellOpen(buffer, context, this, modelFormField, 1);
+                        }
 
-            formStringRenderer.renderFormatHeaderRowCellOpen(buffer, context, this, modelFormField);
+                        // render title (unless this is a submit or a reset field)
+                        formStringRenderer.renderFieldTitle(buffer, context, modelFormField);
 
-            formStringRenderer.renderFieldTitle(buffer, context, modelFormField);
+                        if (separateColumns || modelFormField.getSeparateColumn()) {
+                            formStringRenderer.renderFormatItemRowCellClose(buffer, context, this, modelFormField);
+                        }
 
-            formStringRenderer.renderFormatHeaderRowCellClose(buffer, context, this, modelFormField);
+                        if (innerFormFieldsIt.hasNext()) {
+                            // TODO: determine somehow if this is the last one... how?
+                           if (!separateColumns && !modelFormField.getSeparateColumn()) {
+                                formStringRenderer.renderFormatHeaderRowFormCellTitleSeparator(buffer, context, this, modelFormField, false);
+                           }
+                        }
+                    }
+                    formStringRenderer.renderFormatHeaderRowFormCellClose(buffer, context, this);
+                }
+                Iterator innerDisplayHyperlinkFieldsEndIt = innerDisplayHyperlinkFieldsEnd.iterator();
+                while (innerDisplayHyperlinkFieldsEndIt.hasNext()) {
+                    ModelFormField modelFormField = (ModelFormField) innerDisplayHyperlinkFieldsEndIt.next();
+                    // span columns only if this is the last column in the row (not just in this first list)
+                    if (innerDisplayHyperlinkFieldsEndIt.hasNext() || numOfCells > innerDisplayHyperlinkFieldsEnd.size()) {
+                        formStringRenderer.renderFormatHeaderRowCellOpen(buffer, context, this, modelFormField, 1);
+                    } else {
+                        formStringRenderer.renderFormatHeaderRowCellOpen(buffer, context, this, modelFormField, numOfColumnsToSpan);
+                    }
+                    formStringRenderer.renderFieldTitle(buffer, context, modelFormField);
+                    formStringRenderer.renderFormatHeaderRowCellClose(buffer, context, this, modelFormField);
+                }
+                formStringRenderer.renderFormatHeaderRowClose(buffer, context, this);
+            }
         }
 
-        formStringRenderer.renderFormatHeaderRowClose(buffer, context, this);
+        return maxNumOfColumns;
     }
 
     protected Object safeNext(Iterator iterator) {
@@ -1108,7 +1182,7 @@
         }
     }
     
-    public void renderItemRows(StringBuffer buffer, Map context, FormStringRenderer formStringRenderer, boolean formPerItem) {
+    public void renderItemRows(StringBuffer buffer, Map context, FormStringRenderer formStringRenderer, boolean formPerItem, int numOfColumns) {
         this.rowCount = 0;
         String lookupName = this.getListName();
         if (UtilValidate.isEmpty(lookupName)) {
@@ -1151,6 +1225,7 @@
             // render item rows
             int itemIndex = -1;
             Object item = null;
+            Map previousItem = FastMap.newInstance();
             while ((item = this.safeNext(iter)) != null) {
                 itemIndex++;
                 if (itemIndex >= highIndex) {
@@ -1169,26 +1244,31 @@
                     Map itemMap = (Map) item;
                     localContext.putAll(itemMap);
                 }
-                
+
+                localContext.put("previousItem", previousItem);
+                previousItem = FastMap.newInstance();
+                previousItem.putAll((Map)item);
+
                 ModelFormAction.runSubActions(this.rowActions, localContext);
 
                 localContext.put("itemIndex", new Integer(itemIndex - lowIndex));
                 this.resetBshInterpreter(localContext);
-                this.rowCount++;
 
                 if (Debug.verboseOn()) Debug.logVerbose("In form got another row, context is: " + localContext, module);
 
                 // Check to see if there is a field, same name and same use-when (could come from extended form)
-                for (int j = 0; j < this.fieldList.size(); j++) {
-                    ModelFormField modelFormField = (ModelFormField) this.fieldList.get(j);
+                List tempFieldList = FastList.newInstance();
+                tempFieldList.addAll(this.fieldList);
+                for (int j = 0; j < tempFieldList.size(); j++) {
+                    ModelFormField modelFormField = (ModelFormField) tempFieldList.get(j);
                     if (!modelFormField.isUseWhenEmpty()) {
                         boolean shouldUse1 = modelFormField.shouldUse(localContext);
-                        for (int i = j+1; i < this.fieldList.size(); i++) {
-                            ModelFormField curField = (ModelFormField) this.fieldList.get(i);
+                        for (int i = j+1; i < tempFieldList.size(); i++) {
+                            ModelFormField curField = (ModelFormField) tempFieldList.get(i);
                             if (curField.getName() != null && curField.getName().equals(modelFormField.getName())) {
                                 boolean shouldUse2 = curField.shouldUse(localContext);
                                 if (shouldUse1 == shouldUse2) {
-                                    this.fieldList.remove(i--);
+                                    tempFieldList.remove(i--);
                                 }
                             } else {
                                 continue;
@@ -1196,110 +1276,110 @@
                         }
                     }
                 }
-                
-                // render row formatting open
-                formStringRenderer.renderFormatItemRowOpen(buffer, localContext, this);
 
-                // do the first part of display and hyperlink fields
-                Iterator innerDisplayHyperlinkFieldIter = this.fieldList.iterator();
-                while (innerDisplayHyperlinkFieldIter.hasNext()) {
-                    ModelFormField modelFormField = (ModelFormField) innerDisplayHyperlinkFieldIter.next();
-                    ModelFormField.FieldInfo fieldInfo = modelFormField.getFieldInfo();
+                // Each single item is rendered in one or more rows if its fields have
+                // different "position" attributes. All the fields with the same position
+                // are rendered in the same row.
+                // The default position is 1, and represents the main row:
+                // it contains the fields that are in the list header (columns).
+                // The positions lower than 1 are rendered in rows before the main one;
+                // positions higher than 1 are rendered after the main one.
+
+                // We get a sorted (by position, ascending) set of lists;
+                // each list contains all the fields with that position.
+                Collection fieldListsByPosition = this.getFieldListsByPosition(tempFieldList);
+                Iterator fieldListsByPositionIter = fieldListsByPosition.iterator();
+                //List hiddenIgnoredFieldList = getHiddenIgnoredFields(localContext, null, tempFieldList);
+                while (fieldListsByPositionIter.hasNext()) {
+                    // For each position (the subset of fields with the same position attribute)
+                    // we have two phases: preprocessing and rendering
+                    this.rowCount++;
+                    List fieldListByPosition = (List) fieldListsByPositionIter.next();
+
+                    List innerDisplayHyperlinkFieldsBegin = FastList.newInstance();
+                    List innerFormFields = FastList.newInstance();
+                    List innerDisplayHyperlinkFieldsEnd = FastList.newInstance();
+
+                    // Preprocessing:
+                    // all the form fields are evaluated and the ones that will
+                    // appear in the form are put into three separate lists:
+                    // - hyperlink fields that will appear at the beginning of the row
+                    // - fields of other types
+                    // - hyperlink fields that will appear at the end of the row
+                    Iterator innerDisplayHyperlinkFieldIter = fieldListByPosition.iterator();
+                    int currentPosition = 1;
+                    while (innerDisplayHyperlinkFieldIter.hasNext()) {
+                        ModelFormField modelFormField = (ModelFormField) innerDisplayHyperlinkFieldIter.next();
+                        ModelFormField.FieldInfo fieldInfo = modelFormField.getFieldInfo();
 
-                    // don't do any header for hidden or ignored fields
-                    if (fieldInfo.getFieldType() == ModelFormField.FieldInfo.HIDDEN || fieldInfo.getFieldType() == ModelFormField.FieldInfo.IGNORED) {
-                        continue;
-                    }
+                        // don't do any header for hidden or ignored fields
+                        if (fieldInfo.getFieldType() == ModelFormField.FieldInfo.HIDDEN || fieldInfo.getFieldType() == ModelFormField.FieldInfo.IGNORED) {
+                            continue;
+                        }
 
-                    if (fieldInfo.getFieldType() != ModelFormField.FieldInfo.DISPLAY && fieldInfo.getFieldType() != ModelFormField.FieldInfo.DISPLAY_ENTITY && fieldInfo.getFieldType() != ModelFormField.FieldInfo.HYPERLINK) {
-                        // okay, now do the form cell
-                        break;
-                    }
+                        if (fieldInfo.getFieldType() != ModelFormField.FieldInfo.DISPLAY && fieldInfo.getFieldType() != ModelFormField.FieldInfo.DISPLAY_ENTITY && fieldInfo.getFieldType() != ModelFormField.FieldInfo.HYPERLINK) {
+                            // okay, now do the form cell
+                            break;
+                        }
 
-                    if (!modelFormField.shouldUse(localContext)) {
-                        continue;
+                        if (!modelFormField.shouldUse(localContext)) {
+                            continue;
+                        }
+                        innerDisplayHyperlinkFieldsBegin.add(modelFormField);
+                        currentPosition = modelFormField.getPosition();
                     }
+                    Iterator innerFormFieldIter = fieldListByPosition.iterator();
+                    while (innerFormFieldIter.hasNext()) {
+                        ModelFormField modelFormField = (ModelFormField) innerFormFieldIter.next();
+                        ModelFormField.FieldInfo fieldInfo = modelFormField.getFieldInfo();
 
-                    formStringRenderer.renderFormatItemRowCellOpen(buffer, localContext, this, modelFormField);
-
-                    modelFormField.renderFieldString(buffer, localContext, formStringRenderer);
-
-                    formStringRenderer.renderFormatItemRowCellClose(buffer, localContext, this, modelFormField);
-                }
-
-                // render the "form" cell
-                formStringRenderer.renderFormatItemRowFormCellOpen(buffer, localContext, this);
-
-                if (formPerItem) {
-                    formStringRenderer.renderFormOpen(buffer, localContext, this);
-                }
-
-                // do all of the hidden fields...
-                this.renderHiddenIgnoredFields(buffer, localContext, formStringRenderer, null);
-
-                Iterator innerFormFieldIter = this.fieldList.iterator();
-                while (innerFormFieldIter.hasNext()) {
-                    ModelFormField modelFormField = (ModelFormField) innerFormFieldIter.next();
-                    ModelFormField.FieldInfo fieldInfo = modelFormField.getFieldInfo();
-
-                    // don't do any header for hidden or ignored fields
-                    if (fieldInfo.getFieldType() == ModelFormField.FieldInfo.HIDDEN || fieldInfo.getFieldType() == ModelFormField.FieldInfo.IGNORED) {
-                        continue;
-                    }
+                        // don't do any header for hidden or ignored fields
+                        if (fieldInfo.getFieldType() == ModelFormField.FieldInfo.HIDDEN || fieldInfo.getFieldType() == ModelFormField.FieldInfo.IGNORED) {
+                            continue;
+                        }
 
-                    // skip all of the display/hyperlink fields
-                    if (fieldInfo.getFieldType() == ModelFormField.FieldInfo.DISPLAY || fieldInfo.getFieldType() == ModelFormField.FieldInfo.DISPLAY_ENTITY || fieldInfo.getFieldType() == ModelFormField.FieldInfo.HYPERLINK) {
-                        continue;
-                    }
+                        // skip all of the display/hyperlink fields
+                        if (fieldInfo.getFieldType() == ModelFormField.FieldInfo.DISPLAY || fieldInfo.getFieldType() == ModelFormField.FieldInfo.DISPLAY_ENTITY || fieldInfo.getFieldType() == ModelFormField.FieldInfo.HYPERLINK) {
+                            continue;
+                        }
 
-                    if (!modelFormField.shouldUse(localContext)) {
-                        continue;
+                        if (!modelFormField.shouldUse(localContext)) {
+                            continue;
+                        }
+                        innerFormFields.add(modelFormField);
+                        currentPosition = modelFormField.getPosition();
                     }
+                    while (innerDisplayHyperlinkFieldIter.hasNext()) {
+                        ModelFormField modelFormField = (ModelFormField) innerDisplayHyperlinkFieldIter.next();
+                        ModelFormField.FieldInfo fieldInfo = modelFormField.getFieldInfo();
 
-                    if (separateColumns || modelFormField.getSeparateColumn())
-                        formStringRenderer.renderFormatItemRowCellOpen(buffer, localContext, this, modelFormField);
-                    // render field widget
-                    modelFormField.renderFieldString(buffer, localContext, formStringRenderer);
-
-                    if (separateColumns || modelFormField.getSeparateColumn())
-                        formStringRenderer.renderFormatItemRowCellClose(buffer, localContext, this, modelFormField);
-                }
-
-                if (formPerItem) {
-                    formStringRenderer.renderFormClose(buffer, localContext, this);
-                }
-
-                formStringRenderer.renderFormatItemRowFormCellClose(buffer, localContext, this);
-
-                // render the rest of the display/hyperlink fields
-                while (innerDisplayHyperlinkFieldIter.hasNext()) {
-                    ModelFormField modelFormField = (ModelFormField) innerDisplayHyperlinkFieldIter.next();
-                    ModelFormField.FieldInfo fieldInfo = modelFormField.getFieldInfo();
+                        // don't do any header for hidden or ignored fields
+                        if (fieldInfo.getFieldType() == ModelFormField.FieldInfo.HIDDEN || fieldInfo.getFieldType() == ModelFormField.FieldInfo.IGNORED) {
+                            continue;
+                        }
 
-                    // don't do any header for hidden or ignored fields
-                    if (fieldInfo.getFieldType() == ModelFormField.FieldInfo.HIDDEN || fieldInfo.getFieldType() == ModelFormField.FieldInfo.IGNORED) {
-                        continue;
-                    }
+                        // skip all non-display and non-hyperlink fields
+                        if (fieldInfo.getFieldType() != ModelFormField.FieldInfo.DISPLAY && fieldInfo.getFieldType() != ModelFormField.FieldInfo.DISPLAY_ENTITY && fieldInfo.getFieldType() != ModelFormField.FieldInfo.HYPERLINK) {
+                            continue;
+                        }
 
-                    // skip all non-display and non-hyperlink fields
-                    if (fieldInfo.getFieldType() != ModelFormField.FieldInfo.DISPLAY && fieldInfo.getFieldType() != ModelFormField.FieldInfo.DISPLAY_ENTITY && fieldInfo.getFieldType() != ModelFormField.FieldInfo.HYPERLINK) {
-                        continue;
+                        if (!modelFormField.shouldUse(localContext)) {
+                            continue;
+                        }
+                        innerDisplayHyperlinkFieldsEnd.add(modelFormField);
+                        currentPosition = modelFormField.getPosition();
                     }
+                    List hiddenIgnoredFieldList = getHiddenIgnoredFields(localContext, null, tempFieldList, currentPosition);
 
-                    if (!modelFormField.shouldUse(localContext)) {
-                        continue;
+                    // Rendering:
+                    // the fields in the three lists created in the preprocessing phase
+                    // are now rendered: this will create a visual representation
+                    // of one row (for the current position).
+                    if (innerDisplayHyperlinkFieldsBegin.size() > 0 || innerFormFields.size() > 0 || innerDisplayHyperlinkFieldsEnd.size() > 0) {
+                        this.renderItemRow(buffer, localContext, formStringRenderer, formPerItem, hiddenIgnoredFieldList, innerDisplayHyperlinkFieldsBegin, innerFormFields, innerDisplayHyperlinkFieldsEnd, currentPosition, numOfColumns);
                     }
-
-                    formStringRenderer.renderFormatItemRowCellOpen(buffer, localContext, this, modelFormField);
-
-                    modelFormField.renderFieldString(buffer, localContext, formStringRenderer);
-
-                    formStringRenderer.renderFormatItemRowCellClose(buffer, localContext, this, modelFormField);
-                }
-
-                // render row formatting close
-                formStringRenderer.renderFormatItemRowClose(buffer, localContext, this);
-            }
+                } // iteration on positions
+            } // iteration on items
 
             // reduce the highIndex if number of items falls short
             if ((itemIndex + 1) < highIndex) {
@@ -1319,10 +1399,92 @@
         }
     }
 
-    public void renderHiddenIgnoredFields(StringBuffer buffer, Map context, FormStringRenderer formStringRenderer, Set alreadyRendered) {
-        Iterator fieldIter = this.fieldList.iterator();
+    // The fields in the three lists, usually created in the preprocessing phase
+    // of the renderItemRows method are rendered: this will create a visual representation
+    // of one row (corresponding to one position).
+    public void renderItemRow(StringBuffer buffer, Map localContext, FormStringRenderer formStringRenderer, boolean formPerItem, List hiddenIgnoredFieldList, List innerDisplayHyperlinkFieldsBegin, List innerFormFields, List innerDisplayHyperlinkFieldsEnd, int position, int numOfColumns) {
+        int numOfCells = innerDisplayHyperlinkFieldsBegin.size() +
+                         innerDisplayHyperlinkFieldsEnd.size() +
+                         ((hiddenIgnoredFieldList.size() > 0 || innerFormFields.size() > 0)? 1: 0);
+        int numOfColumnsToSpan = numOfColumns - numOfCells + 1;
+        if (numOfColumnsToSpan < 1) {
+            numOfColumnsToSpan = 1;
+        }
+        // render row formatting open
+        formStringRenderer.renderFormatItemRowOpen(buffer, localContext, this);
+
+        // do the first part of display and hyperlink fields
+        Iterator innerDisplayHyperlinkFieldIter = innerDisplayHyperlinkFieldsBegin.iterator();
+        while (innerDisplayHyperlinkFieldIter.hasNext()) {
+            ModelFormField modelFormField = (ModelFormField) innerDisplayHyperlinkFieldIter.next();
+            // span columns only if this is the last column in the row (not just in this first list)
+            if (innerDisplayHyperlinkFieldIter.hasNext() || numOfCells > innerDisplayHyperlinkFieldsBegin.size()) {
+                formStringRenderer.renderFormatItemRowCellOpen(buffer, localContext, this, modelFormField, 1);
+            } else {
+                formStringRenderer.renderFormatItemRowCellOpen(buffer, localContext, this, modelFormField, numOfColumnsToSpan);
+            }
+            modelFormField.renderFieldString(buffer, localContext, formStringRenderer);
+            formStringRenderer.renderFormatItemRowCellClose(buffer, localContext, this, modelFormField);
+        }
+
+        if (hiddenIgnoredFieldList.size() > 0 || innerFormFields.size() > 0) {
+            // render the "form" cell
+            formStringRenderer.renderFormatItemRowFormCellOpen(buffer, localContext, this); // TODO: colspan
+
+            if (formPerItem) {
+                formStringRenderer.renderFormOpen(buffer, localContext, this);
+            }
+
+            // do all of the hidden fields...
+            this.renderHiddenIgnoredFields(buffer, localContext, formStringRenderer, hiddenIgnoredFieldList);
+
+            Iterator innerFormFieldIter = innerFormFields.iterator();
+            while (innerFormFieldIter.hasNext()) {
+                ModelFormField modelFormField = (ModelFormField) innerFormFieldIter.next();
+                if (separateColumns || modelFormField.getSeparateColumn()) {
+                    formStringRenderer.renderFormatItemRowCellOpen(buffer, localContext, this, modelFormField, 1);
+                }
+                // render field widget
+                modelFormField.renderFieldString(buffer, localContext, formStringRenderer);
+                if (separateColumns || modelFormField.getSeparateColumn()) {
+                    formStringRenderer.renderFormatItemRowCellClose(buffer, localContext, this, modelFormField);
+                }
+            }
+
+            if (formPerItem) {
+                formStringRenderer.renderFormClose(buffer, localContext, this);
+            }
+
+            formStringRenderer.renderFormatItemRowFormCellClose(buffer, localContext, this);
+        }
+
+        // render the rest of the display/hyperlink fields
+        innerDisplayHyperlinkFieldIter = innerDisplayHyperlinkFieldsEnd.iterator();
+        while (innerDisplayHyperlinkFieldIter.hasNext()) {
+            ModelFormField modelFormField = (ModelFormField) innerDisplayHyperlinkFieldIter.next();
+            // span columns only if this is the last column in the row
+            if (innerDisplayHyperlinkFieldIter.hasNext()) {
+                formStringRenderer.renderFormatItemRowCellOpen(buffer, localContext, this, modelFormField, 1);
+            } else {
+                formStringRenderer.renderFormatItemRowCellOpen(buffer, localContext, this, modelFormField, numOfColumnsToSpan);
+            }
+            modelFormField.renderFieldString(buffer, localContext, formStringRenderer);
+            formStringRenderer.renderFormatItemRowCellClose(buffer, localContext, this, modelFormField);
+        }
+
+        // render row formatting close
+        formStringRenderer.renderFormatItemRowClose(buffer, localContext, this);
+    }
+
+    public List getHiddenIgnoredFields(Map context, Set alreadyRendered, List fieldList, int position) {
+        List hiddenIgnoredFieldList = FastList.newInstance();
+        Iterator fieldIter = fieldList.iterator();
         while (fieldIter.hasNext()) {
             ModelFormField modelFormField = (ModelFormField) fieldIter.next();
+            // with position == -1 then gets all the hidden fields
+            if (position != -1 && modelFormField.getPosition() != position) {
+                continue;
+            }
             ModelFormField.FieldInfo fieldInfo = modelFormField.getFieldInfo();
 
             // render hidden/ignored field widget
@@ -1330,7 +1492,7 @@
                 case ModelFormField.FieldInfo.HIDDEN :
                 case ModelFormField.FieldInfo.IGNORED :
                     if (modelFormField.shouldUse(context)) {
-                        modelFormField.renderFieldString(buffer, context, formStringRenderer);
+                        hiddenIgnoredFieldList.add(modelFormField);
                         if (alreadyRendered != null)
                             alreadyRendered.add(modelFormField.getName());
                     }
@@ -1340,7 +1502,7 @@
                 case ModelFormField.FieldInfo.DISPLAY_ENTITY :
                     ModelFormField.DisplayField displayField = (ModelFormField.DisplayField) fieldInfo;
                     if (displayField.getAlsoHidden() && modelFormField.shouldUse(context)) {
-                        formStringRenderer.renderHiddenField(buffer, context, modelFormField, modelFormField.getEntry(context));
+                        hiddenIgnoredFieldList.add(modelFormField);
                         // don't add to already rendered here, or the display won't ger rendered: if (alreadyRendered != null) alreadyRendered.add(modelFormField.getName());
                     }
                     break;
@@ -1348,12 +1510,62 @@
                 case ModelFormField.FieldInfo.HYPERLINK :
                     ModelFormField.HyperlinkField hyperlinkField = (ModelFormField.HyperlinkField) fieldInfo;
                     if (hyperlinkField.getAlsoHidden() && modelFormField.shouldUse(context)) {
-                        formStringRenderer.renderHiddenField(buffer, context, modelFormField, modelFormField.getEntry(context));
+                        hiddenIgnoredFieldList.add(modelFormField);
                         // don't add to already rendered here, or the hyperlink won't ger rendered: if (alreadyRendered != null) alreadyRendered.add(modelFormField.getName());
                     }
                     break;
             }
         }
+        return hiddenIgnoredFieldList;
+    }
+    public void renderHiddenIgnoredFields(StringBuffer buffer, Map context, FormStringRenderer formStringRenderer, List fieldList) {
+        Iterator fieldIter = fieldList.iterator();
+        while (fieldIter.hasNext()) {
+            ModelFormField modelFormField = (ModelFormField) fieldIter.next();
+            ModelFormField.FieldInfo fieldInfo = modelFormField.getFieldInfo();
+
+            // render hidden/ignored field widget
+            switch (fieldInfo.getFieldType()) {
+                case ModelFormField.FieldInfo.HIDDEN :
+                case ModelFormField.FieldInfo.IGNORED :
+                    modelFormField.renderFieldString(buffer, context, formStringRenderer);
+                    break;
+
+                case ModelFormField.FieldInfo.DISPLAY :
+                case ModelFormField.FieldInfo.DISPLAY_ENTITY :
+                case ModelFormField.FieldInfo.HYPERLINK :
+                    formStringRenderer.renderHiddenField(buffer, context, modelFormField, modelFormField.getEntry(context));
+                    break;
+            }
+        }
+    }
+
+    public Collection getFieldListsByPosition(List modelFormFieldList) {
+        Map fieldsByPosition = new TreeMap();
+        Iterator fieldListIter = modelFormFieldList.iterator();
+        while (fieldListIter.hasNext()) {
+            ModelFormField modelFormField = (ModelFormField) fieldListIter.next();
+            Integer position = new Integer(modelFormField.getPosition());
+            List fieldListByPosition = (List)fieldsByPosition.get(position);
+            if (fieldListByPosition == null) {
+                fieldListByPosition = FastList.newInstance();
+                fieldsByPosition.put(position, fieldListByPosition);
+            }
+            fieldListByPosition.add(modelFormField);
+        }
+        return fieldsByPosition.values();
+    }
+
+    public List getFieldListByPosition(List modelFormFieldList, int position) {
+        List fieldListByPosition = FastList.newInstance();
+        Iterator fieldListIter = modelFormFieldList.iterator();
+        while (fieldListIter.hasNext()) {
+            ModelFormField modelFormField = (ModelFormField) fieldListIter.next();
+            if (modelFormField.getPosition() == position) {
+                fieldListByPosition.add(modelFormField);
+            }
+        }
+        return fieldListByPosition;
     }
 
     public LocalDispatcher getDispacher() {
@@ -2161,10 +2373,23 @@
         public String serviceName;
         public String mapName;
         public String defaultFieldType;
+        public int defaultPosition;
         public AutoFieldsService(Element element) {
             this.serviceName = element.getAttribute("service-name");
             this.mapName = element.getAttribute("map-name");
             this.defaultFieldType = element.getAttribute("default-field-type");
+            String positionStr = element.getAttribute("default-position");
+            int position = 1;
+            try {
+                if (positionStr != null && positionStr.length() > 0) {
+                    position = Integer.valueOf(positionStr);
+                }
+            } catch (Exception e) {
+                Debug.logError(e,
+                    "Could not convert position attribute of the field element to an integer: [" + positionStr + "], using the default of the form renderer",
+                    module);
+            }
+            this.defaultPosition = position;
         }
     }
 
@@ -2172,10 +2397,23 @@
         public String entityName;
         public String mapName;
         public String defaultFieldType;
+        public int defaultPosition;
         public AutoFieldsEntity(Element element) {
             this.entityName = element.getAttribute("entity-name");
             this.mapName = element.getAttribute("map-name");
             this.defaultFieldType = element.getAttribute("default-field-type");
+            String positionStr = element.getAttribute("default-position");
+            int position = 1;
+            try {
+                if (positionStr != null && positionStr.length() > 0) {
+                    position = Integer.valueOf(positionStr);
+                }
+            } catch (Exception e) {
+                Debug.logError(e,
+                    "Could not convert position attribute of the field element to an integer: [" + positionStr + "], using the default of the form renderer",
+                    module);
+            }
+            this.defaultPosition = position;
         }
     }
 

Modified: ofbiz/trunk/framework/widget/src/org/ofbiz/widget/form/ModelFormField.java
URL: http://svn.apache.org/viewvc/ofbiz/trunk/framework/widget/src/org/ofbiz/widget/form/ModelFormField.java?view=diff&rev=550168&r1=550167&r2=550168
==============================================================================
--- ofbiz/trunk/framework/widget/src/org/ofbiz/widget/form/ModelFormField.java (original)
+++ ofbiz/trunk/framework/widget/src/org/ofbiz/widget/form/ModelFormField.java Sat Jun 23 22:09:41 2007
@@ -228,7 +228,7 @@
             this.fieldName = overrideFormField.fieldName;
         if (UtilValidate.isNotEmpty(overrideFormField.attributeName))
             this.attributeName = overrideFormField.attributeName;
-        if (overrideFormField.title != null && !overrideFormField.title.isEmpty())
+        if (overrideFormField.title != null && overrideFormField.title.getOriginal() != null) // title="" can be used to override the original value
             this.title = overrideFormField.title;
         if (overrideFormField.tooltip != null && !overrideFormField.tooltip.isEmpty())
             this.tooltip = overrideFormField.tooltip;

Modified: ofbiz/trunk/framework/widget/src/org/ofbiz/widget/html/HtmlFormRenderer.java
URL: http://svn.apache.org/viewvc/ofbiz/trunk/framework/widget/src/org/ofbiz/widget/html/HtmlFormRenderer.java?view=diff&rev=550168&r1=550167&r2=550168
==============================================================================
--- ofbiz/trunk/framework/widget/src/org/ofbiz/widget/html/HtmlFormRenderer.java (original)
+++ ofbiz/trunk/framework/widget/src/org/ofbiz/widget/html/HtmlFormRenderer.java Sat Jun 23 22:09:41 2007
@@ -1191,11 +1191,16 @@
     }
 
     /* (non-Javadoc)
-     * @see org.ofbiz.widget.form.FormStringRenderer#renderFormatHeaderRowCellOpen(java.lang.StringBuffer, java.util.Map, org.ofbiz.widget.form.ModelForm, org.ofbiz.widget.form.ModelFormField)
+     * @see org.ofbiz.widget.form.FormStringRenderer#renderFormatHeaderRowCellOpen(java.lang.StringBuffer, java.util.Map, org.ofbiz.widget.form.ModelForm, org.ofbiz.widget.form.ModelFormField, int positionSpan)
      */
-    public void renderFormatHeaderRowCellOpen(StringBuffer buffer, Map context, ModelForm modelForm, ModelFormField modelFormField) {
+    public void renderFormatHeaderRowCellOpen(StringBuffer buffer, Map context, ModelForm modelForm, ModelFormField modelFormField, int positionSpan) {
         buffer.append("   <td");
         String areaStyle = modelFormField.getTitleAreaStyle();
+        if (positionSpan > 1) {
+            buffer.append(" colspan=\"");
+            buffer.append(positionSpan);
+            buffer.append("\"");
+        }
         if (UtilValidate.isNotEmpty(areaStyle)) {
             buffer.append(" class=\"");
             buffer.append(areaStyle);
@@ -1295,9 +1300,14 @@
     /* (non-Javadoc)
      * @see org.ofbiz.widget.form.FormStringRenderer#renderFormatItemRowCellOpen(java.lang.StringBuffer, java.util.Map, org.ofbiz.widget.form.ModelForm, org.ofbiz.widget.form.ModelFormField)
      */
-    public void renderFormatItemRowCellOpen(StringBuffer buffer, Map context, ModelForm modelForm, ModelFormField modelFormField) {
+    public void renderFormatItemRowCellOpen(StringBuffer buffer, Map context, ModelForm modelForm, ModelFormField modelFormField, int positionSpan) {
         buffer.append("   <td");
         String areaStyle = modelFormField.getWidgetAreaStyle();
+        if (positionSpan > 1) {
+            buffer.append(" colspan=\"");
+            buffer.append(positionSpan);
+            buffer.append("\"");
+        }
         if (UtilValidate.isNotEmpty(areaStyle)) {
             buffer.append(" class=\"");
             buffer.append(areaStyle);

Modified: ofbiz/trunk/framework/widget/src/org/ofbiz/widget/xml/XmlFormRenderer.java
URL: http://svn.apache.org/viewvc/ofbiz/trunk/framework/widget/src/org/ofbiz/widget/xml/XmlFormRenderer.java?view=diff&rev=550168&r1=550167&r2=550168
==============================================================================
--- ofbiz/trunk/framework/widget/src/org/ofbiz/widget/xml/XmlFormRenderer.java (original)
+++ ofbiz/trunk/framework/widget/src/org/ofbiz/widget/xml/XmlFormRenderer.java Sat Jun 23 22:09:41 2007
@@ -193,7 +193,7 @@
     public void renderFormatHeaderRowClose(StringBuffer buffer, Map context, ModelForm modelForm) {
     }
 
-    public void renderFormatHeaderRowCellOpen(StringBuffer buffer, Map context, ModelForm modelForm, ModelFormField modelFormField) {
+    public void renderFormatHeaderRowCellOpen(StringBuffer buffer, Map context, ModelForm modelForm, ModelFormField modelFormField, int positionSpan) {
     }
 
     public void renderFormatHeaderRowCellClose(StringBuffer buffer, Map context, ModelForm modelForm, ModelFormField modelFormField) {
@@ -222,7 +222,7 @@
         this.appendWhitespace(buffer);
     }
 
-    public void renderFormatItemRowCellOpen(StringBuffer buffer, Map context, ModelForm modelForm, ModelFormField modelFormField) {
+    public void renderFormatItemRowCellOpen(StringBuffer buffer, Map context, ModelForm modelForm, ModelFormField modelFormField, int positionSpan) {
         buffer.append("<");
         buffer.append(modelFormField.getName());
         buffer.append(">");