summaryrefslogtreecommitdiffstats
path: root/forms/qa/integration
diff options
context:
space:
mode:
Diffstat (limited to 'forms/qa/integration')
-rw-r--r--forms/qa/integration/forms/BooleanValidator.java64
-rw-r--r--forms/qa/integration/forms/CellBinding.java543
-rw-r--r--forms/qa/integration/forms/ControlValidation.java176
-rw-r--r--forms/qa/integration/forms/ControlValidator.java45
-rw-r--r--forms/qa/integration/forms/DateValidator.java80
-rw-r--r--forms/qa/integration/forms/DocumentHelper.java375
-rw-r--r--forms/qa/integration/forms/DocumentType.java39
-rw-r--r--forms/qa/integration/forms/DocumentViewHelper.java221
-rw-r--r--forms/qa/integration/forms/FormComponent.java114
-rw-r--r--forms/qa/integration/forms/FormControlTest.java951
-rw-r--r--forms/qa/integration/forms/FormLayer.java318
-rw-r--r--forms/qa/integration/forms/FormPropertyBags.java201
-rw-r--r--forms/qa/integration/forms/ImageComparison.java79
-rw-r--r--forms/qa/integration/forms/ListBox.java285
-rw-r--r--forms/qa/integration/forms/ListSelection.java280
-rw-r--r--forms/qa/integration/forms/ListSelection.props1
-rw-r--r--forms/qa/integration/forms/ListSelectionValidator.java51
-rw-r--r--forms/qa/integration/forms/MasterDetailForms.java400
-rw-r--r--forms/qa/integration/forms/NumericValidator.java68
-rw-r--r--forms/qa/integration/forms/RadioButtons.java426
-rw-r--r--forms/qa/integration/forms/SingleControlValidation.java170
-rw-r--r--forms/qa/integration/forms/SpreadsheetDocument.java68
-rw-r--r--forms/qa/integration/forms/SpreadsheetView.java59
-rw-r--r--forms/qa/integration/forms/TableCellTextBinding.java192
-rw-r--r--forms/qa/integration/forms/TestCase.java150
-rw-r--r--forms/qa/integration/forms/TextValidator.java65
-rw-r--r--forms/qa/integration/forms/TimeValidator.java73
-rw-r--r--forms/qa/integration/forms/ValueBinding.java112
-rw-r--r--forms/qa/integration/forms/WaitForInput.java58
-rw-r--r--forms/qa/integration/forms/XMLFormSettings.java213
-rw-r--r--forms/qa/integration/forms/dbfTools.java73
31 files changed, 5950 insertions, 0 deletions
diff --git a/forms/qa/integration/forms/BooleanValidator.java b/forms/qa/integration/forms/BooleanValidator.java
new file mode 100644
index 000000000..9d85a8616
--- /dev/null
+++ b/forms/qa/integration/forms/BooleanValidator.java
@@ -0,0 +1,64 @@
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+package integration.forms;
+
+import com.sun.star.uno.AnyConverter;
+
+public class BooleanValidator extends integration.forms.ControlValidator
+{
+ private final boolean m_preventChecked;
+
+ /** Creates a new instance of BooleanValidator */
+ public BooleanValidator( boolean preventChecked )
+ {
+ m_preventChecked = preventChecked;
+ }
+
+ public String explainInvalid( Object Value )
+ {
+ try
+ {
+ if ( AnyConverter.isVoid( Value ) )
+ return "'indetermined' is not an allowed state";
+ boolean value = ((Boolean)Value).booleanValue();
+ if ( m_preventChecked && ( value ) )
+ return "no no no. Don't check it.";
+ }
+ catch( java.lang.Exception e )
+ {
+ return "ooops. Unknown error";
+ }
+ return "";
+ }
+
+ public boolean isValid( Object Value )
+ {
+ try
+ {
+ if ( AnyConverter.isVoid( Value ) )
+ return false;
+
+ boolean value = ((Boolean)Value).booleanValue();
+ return !(m_preventChecked && ( value ));
+ }
+ catch( java.lang.Exception e )
+ {
+ }
+ return false;
+ }
+}
diff --git a/forms/qa/integration/forms/CellBinding.java b/forms/qa/integration/forms/CellBinding.java
new file mode 100644
index 000000000..56d811a25
--- /dev/null
+++ b/forms/qa/integration/forms/CellBinding.java
@@ -0,0 +1,543 @@
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+package integration.forms;
+
+import com.sun.star.uno.*;
+import com.sun.star.util.*;
+import com.sun.star.lang.*;
+import com.sun.star.beans.*;
+import com.sun.star.form.binding.*;
+import com.sun.star.accessibility.*;
+import com.sun.star.awt.XListBox;
+import com.sun.star.table.CellAddress;
+import com.sun.star.table.XCell;
+import com.sun.star.sheet.XCellRangeData;
+import com.sun.star.sheet.XCellRangeFormula;
+import com.sun.star.text.XTextRange;
+
+public class CellBinding extends complexlib.ComplexTestCase
+{
+ /** the test document our form layer lives in */
+ private SpreadsheetDocument m_document;
+ /** our form layer */
+ private FormLayer m_formLayer;
+
+ @Override
+ public String[] getTestMethodNames()
+ {
+ return new String[] {
+ "checkTextFieldBinding",
+ "checkBooleanRadioBinding",
+ "checkStringRadioBinding",
+ "checkBooleanCheckBoxBinding",
+ "checkStringCheckBoxBinding",
+ "checkListBoxBinding",
+ "checkListBoxIndexBinding"
+ };
+ }
+
+ @Override
+ public String getTestObjectName()
+ {
+ return "Form Control Spreadsheet Cell Binding Test";
+ }
+
+ /* ------------------------------------------------------------------ */
+ /** closes our document, if we have an open one
+ */
+ private void closeDocument()
+ {
+ try
+ {
+ // close our document
+ if ( m_document != null )
+ {
+ XCloseable closeDoc = UnoRuntime.queryInterface( XCloseable.class,
+ m_document.getDocument() );
+ closeDoc.close( true );
+ }
+ }
+ catch ( com.sun.star.uno.Exception e )
+ {
+ }
+ }
+
+ /* ------------------------------------------------------------------ */
+ public void before() throws com.sun.star.uno.Exception, java.lang.Exception
+ {
+ /* our service factory */
+ XMultiServiceFactory orb = param.getMSF();
+ m_document = new SpreadsheetDocument( orb );
+ m_formLayer = new FormLayer( m_document );
+ }
+
+ /* ------------------------------------------------------------------ */
+ public void after() throws java.lang.Exception
+ {
+ closeDocument();
+ }
+
+ /* ------------------------------------------------------------------ */
+ public void checkTextFieldBinding( ) throws com.sun.star.uno.Exception, java.lang.Exception
+ {
+ final short col = 0;
+ final short row = 2;
+ final String text = "content";
+ final String otherText = "something else";
+ final String yetAnotherText = "yet another text";
+
+ // create a normal text control
+ XPropertySet controlModel = m_formLayer.createControlAndShape( "DatabaseTextField", 30, 9, 30, 6 );
+
+ // bind it to cell A1
+ bindToCell( controlModel, col, row );
+
+ // switch to alive mode
+ m_document.getCurrentView().toggleFormDesignMode();
+
+ // test the data transfer control -> cell
+ simulateUserTextInput( controlModel, text );
+ verifyStringCellContent( col, row, text, "A text field does not forward its user input to the cell." );
+
+ // the same, but this time changing the control value programmatically
+ controlModel.setPropertyValue( "Text", otherText );
+ verifyStringCellContent( col, row, otherText, "A text field does not forward programmatic changes to the cell." );
+
+ // the other way round: cell->control
+ setCellText( col, row, yetAnotherText );
+ String controlText = (String)controlModel.getPropertyValue( "Text" );
+ if ( !controlText.equals( yetAnotherText ) )
+ failed( "Changes in the cell are not forwarded to the text field." );
+ }
+ /* ------------------------------------------------------------------ */
+ public void checkBooleanRadioBinding( ) throws com.sun.star.uno.Exception, java.lang.Exception
+ {
+ // two radio buttons
+ XPropertySet primaryRadio = createRadio( 28, "radio button no. 1", "radio group", "primary" );
+ XPropertySet secondaryRadio = createRadio( 33, "radio button no. 2", "radio group", "secodary" );
+
+ // bind them
+ short col = (short)0;
+ short row1 = (short)6;
+ short row2 = (short)7;
+ bindToCell( primaryRadio, col, row1 );
+ bindToCell( secondaryRadio, col, row2 );
+
+ // check the first button
+ simulateUserRadioCheck( primaryRadio );
+ // check the cell content
+ verifyNumericCellContent( col, row1, 1, "Radio buttons do not forward their (boolean) values to cells (1)." );
+ verifyNumericCellContent( col, row2, 0, "Radio buttons do not forward their (boolean) values to cells (2)." );
+ // check the second button
+ simulateUserRadioCheck( secondaryRadio );
+ // check the cell content
+ verifyNumericCellContent( col, row1, 0, "Radio buttons do not forward their (boolean) values to cells (3)." );
+ verifyNumericCellContent( col, row2, 1, "Radio buttons do not forward their (boolean) values to cells (4)." );
+
+ // the other way round: writing values into the cell
+ setCellValue( col, row1, 1.0 );
+ // setting this should have checked the primary radio, which should have unchecked the secondary radio,
+ // which should have been propagated to the second cell
+ verifyNumericCellContent( col, row2, 0, "Changing primary cell is not propagated to the secondary cell (via the radio buttons)." );
+
+ // setting an empty cell should result in the radio being unchecked
+ setCellEmpty( col, row1 );
+ if ( ((Short)primaryRadio.getPropertyValue( "State" )).shortValue() != 0 )
+ failed( "Setting a cell to 'empty' does not reset the bound radio button." );
+ }
+
+ /* ------------------------------------------------------------------ */
+ public void checkStringRadioBinding( ) throws com.sun.star.uno.Exception, java.lang.Exception
+ {
+ // two radio buttons
+ XPropertySet primaryRadio = createRadio( 46, "radio button A", "radio ref group", "primary" );
+ XPropertySet secondaryRadio = createRadio( 51, "radio button B", "radio ref group", "secodary" );
+
+ // give the ref values
+ String refValueA = "ref value A";
+ String refValueB = "ref value B";
+ primaryRadio.setPropertyValue( "RefValue", refValueA );
+ secondaryRadio.setPropertyValue( "RefValue", refValueB );
+
+ // bind them to the same cell
+ short col = (short)0;
+ short row = (short)10;
+ bindToCell( primaryRadio, col, row );
+ bindToCell( secondaryRadio, col, row );
+
+ // checking a radio should set the respective ref value at the cell
+ simulateUserRadioCheck( primaryRadio );
+ verifyStringCellContent( col, row, refValueA, "A bound radio button with a reference value does not pass this value to the cell upon checking (1)." );
+ simulateUserRadioCheck( secondaryRadio );
+ verifyStringCellContent( col, row, refValueB, "A bound radio button with a reference value does not pass this value to the cell upon checking (2)." );
+
+ // changing the cell should check the buttons if the cell text equals the ref value
+ setCellText( col, row, "no ref value" );
+ verifyRadioStates( primaryRadio, secondaryRadio, (short)0, (short)0, "Radio button not unchecked, though the bound cell value does not equal ref value." );
+
+ setCellText( col, row, refValueA );
+ verifyRadioStates( primaryRadio, secondaryRadio, (short)1, (short)0, "Radio button not properly un/checked according to the cell and ref value (1)." );
+
+ setCellText( col, row, refValueB );
+ verifyRadioStates( primaryRadio, secondaryRadio, (short)0, (short)1, "Radio button not properly un/checked according to the cell and ref value (2)." );
+ }
+
+ /* ------------------------------------------------------------------ */
+ public void checkBooleanCheckBoxBinding( ) throws com.sun.star.uno.Exception, java.lang.Exception
+ {
+ XPropertySet checkBox = m_formLayer.createControlAndShape( "DatabaseCheckBox", 30, 59, 40, 4 );
+ checkBox.setPropertyValue( "Label", "check box" );
+ checkBox.setPropertyValue( "TriState", Boolean.TRUE );
+
+ short col = (short)0;
+ short row = (short)13;
+ bindToCell( checkBox, col, row );
+
+ // initialize with "not checked"
+ checkBox.setPropertyValue( "State", Short.valueOf( (short)0 ) );
+ verifyNumericCellContent( col, row, 0, "programmatically unchecking the check box is not propagated to the cell." );
+
+ // first click: "not checked" -> "checked"
+ simulateUserCheckBoxCheck( checkBox, (short)1 );
+ verifyNumericCellContent( col, row, 1, "moving the check box state to 'checked' is not propagated to the cell." );
+
+ // second click: "checked" -> "indetermined"
+ simulateUserCheckBoxCheck( checkBox, (short)2 );
+ verifyVoidCell( col, row, "propagating the 'indetermined' state to the cell does not work." );
+
+ // third click: "indetermined" -> "not checked"
+ simulateUserCheckBoxCheck( checkBox, (short)0 );
+ verifyNumericCellContent( col, row, 0, "unchecking a check box via UI is not propagated to the cell." );
+ }
+
+ /* ------------------------------------------------------------------ */
+ public void checkStringCheckBoxBinding( ) throws com.sun.star.uno.Exception, java.lang.Exception
+ {
+ String refValue = "checked ";
+
+ XPropertySet checkBox = m_formLayer.createControlAndShape( "DatabaseCheckBox", 30, 68, 40, 4 );
+ checkBox.setPropertyValue( "Label", "check box with ref value" );
+ checkBox.setPropertyValue( "TriState", Boolean.TRUE );
+ checkBox.setPropertyValue( "RefValue", refValue );
+
+ short col = (short)0;
+ short row = (short)15;
+ bindToCell( checkBox, col, row );
+
+ // initialize with "not checked"
+ checkBox.setPropertyValue( "State", Short.valueOf( (short)0 ) );
+ verifyNumericCellContent( col, row, 0, "programmatically unchecking the check box is not propagated to the cell." );
+
+ // first click: "not checked" -> "checked"
+ simulateUserCheckBoxCheck( checkBox, (short)1 );
+ verifyStringCellContent( col, row, refValue, "moving the check box state to 'checked' does not propagated the ref value to the cell." );
+
+ // second click: "checked" -> "indetermined"
+ simulateUserCheckBoxCheck( checkBox, (short)2 );
+ verifyVoidCell( col, row, "propagating the 'indetermined' state to the cell does not work, when exchanging ref values." );
+
+ // third click: "indetermined" -> "not checked"
+ simulateUserCheckBoxCheck( checkBox, (short)0 );
+ verifyStringCellContent( col, row, "", "unchecking a check box via UI does not propagated the ref value to the cell." );
+ }
+
+ /* ------------------------------------------------------------------ */
+ /** verifies that a list box, which is bound via an ordinary value binding,
+ * works as expected
+ */
+ public void checkListBoxBinding( ) throws com.sun.star.uno.Exception, java.lang.Exception
+ {
+ XPropertySet listBox = m_formLayer.createControlAndShape( "DatabaseListBox", 30, 80, 40, 6 );
+ listBox.setPropertyValue( "Dropdown", Boolean.TRUE );
+ listBox.setPropertyValue( "StringItemList", new String[] { "Apples", "Oranges", "Peaches" } );
+
+ short col = (short)0;
+ short row = (short)18;
+
+
+ // add a list entry source which fills the list boxes list from cells in the
+ // spreadsheet
+ short sourceCol = (short)4;
+ setCellText( sourceCol, (short)( row - 1 ), "Apples" );
+ setCellText( sourceCol, (short)( row + 0 ), "Oranges" );
+ setCellText( sourceCol, (short)( row + 1 ), "Peaches" );
+
+ // TODO: this is currently prone to deadlocks
+
+
+ // bind to a cell
+ bindToCell( listBox, col, row );
+
+
+ // do the tests
+ listBox.setPropertyValue( "SelectedItems", new short[] { (short)0 } );
+ verifyStringCellContent( col, row, "Apples", "programmatically selecting a list entry is not propagated to the cell." );
+
+ simulateUserListBoxSelection( listBox, "Oranges" );
+ verifyStringCellContent( col, row, "Oranges", "UI-selecting a list entry is not propagated to the cell." );
+
+ setCellText( col, row, "Peaches" );
+ short[] selectedItems = (short[])listBox.getPropertyValue( "SelectedItems" );
+ assureEquals( "changes in the cell bound to a list box are not propagated to the list box selection",
+ 2, selectedItems[0] );
+ }
+
+ /* ------------------------------------------------------------------ */
+ /** verifies that a list box, which is bound via a value binding exchanging the <b>index</b>
+ * of the selected entry, works as expected
+ */
+ public void checkListBoxIndexBinding() throws com.sun.star.uno.Exception, java.lang.Exception
+ {
+ XPropertySet listBox = m_formLayer.createControlAndShape( "DatabaseListBox", 30, 94, 40, 6 );
+ listBox.setPropertyValue( "Dropdown", Boolean.TRUE );
+ listBox.setPropertyValue( "StringItemList", new String[] { "Pears", "Bananas", "Strawberries" } );
+
+ short col = (short)0;
+ short row = (short)21;
+
+
+ // add a list entry source which fills the list boxes list from cells in the
+ // spreadsheet
+ short sourceCol = (short)4;
+ setCellText( sourceCol, (short)( row - 1 ), "Pears" );
+ setCellText( sourceCol, (short)( row + 0 ), "Bananas" );
+ setCellText( sourceCol, (short)( row + 1 ), "Strawberries" );
+
+ // TODO: this is currently prone to deadlocks
+
+
+ // bind to a cell
+ bindToCell( listBox, col, row, "com.sun.star.table.ListPositionCellBinding" );
+
+
+ // do the tests
+ listBox.setPropertyValue( "SelectedItems", new short[] { (short)0 } );
+ verifyNumericCellContent( col, row, 1, "programmatically selecting a list entry is not propagated (as index) to the cell." );
+
+ simulateUserListBoxSelection( listBox, "Bananas" );
+ verifyNumericCellContent( col, row, 2, "UI-selecting a list entry is not propagated (as index) to the cell." );
+
+ setCellValue( col, row, 3 );
+ short[] selectedItems = (short[])listBox.getPropertyValue( "SelectedItems" );
+ assureEquals( "changes in the cell bound to a list box via list index are not propagated to the list box selection",
+ 2, selectedItems[0] );
+ }
+
+ /* ------------------------------------------------------------------ */
+ /** verifies that the content of a given cell equals a given string
+ */
+ private XPropertySet createRadio( int yPos, String label, String name, String tag ) throws com.sun.star.uno.Exception, java.lang.Exception
+ {
+ XPropertySet radio = m_formLayer.createControlAndShape( "DatabaseRadioButton", 30, yPos, 40, 4 );
+ radio.setPropertyValue( "Label", label );
+ radio.setPropertyValue( "Name", name );
+ radio.setPropertyValue( "Tag", tag );
+ return radio;
+ }
+
+ /* ------------------------------------------------------------------ */
+ /** verifies the states of two radio button
+ */
+ private boolean verifyRadioStates( XPropertySet radio1, XPropertySet radio2, short value1, short value2,
+ String errorMessage ) throws com.sun.star.uno.Exception, java.lang.Exception
+ {
+ if ( ( ((Short)radio1.getPropertyValue( "State" )).shortValue() != value1 )
+ || ( ((Short)radio2.getPropertyValue( "State" )).shortValue() != value2 )
+ )
+ {
+ failed( errorMessage );
+ return false;
+ }
+ return true;
+ }
+
+ /* ------------------------------------------------------------------ */
+ /** verifies that the content of a given cell equals a given string
+ */
+ private boolean verifyVoidCell( short col, short row, String failErrorMessage ) throws com.sun.star.uno.Exception
+ {
+ XCellRangeData cell = UnoRuntime.queryInterface( XCellRangeData.class,
+ m_document.getSheet( 0 ).getCellByPosition( col, row )
+ );
+ Object cellContent = cell.getDataArray()[0][0];
+ if ( ((com.sun.star.uno.Any)cellContent).getType().getTypeClass() != com.sun.star.uno.TypeClass.VOID )
+ {
+ failed( failErrorMessage );
+ return false;
+ }
+ return true;
+ }
+
+ /* ------------------------------------------------------------------ */
+ /** verifies that the content of a given cell equals a given string
+ */
+ private boolean verifyNumericCellContent( short col, short row, double value, String failErrorMessage ) throws com.sun.star.uno.Exception
+ {
+ XCell cell = UnoRuntime.queryInterface( XCell.class,
+ m_document.getSheet( 0 ).getCellByPosition( col, row )
+ );
+ if ( cell.getValue() != value )
+ {
+ failed( failErrorMessage );
+ return false;
+ }
+ return true;
+ }
+
+ /* ------------------------------------------------------------------ */
+ /** verifies that the content of a given cell equals a given string
+ */
+ private boolean verifyStringCellContent( short col, short row, String text, String failErrorMessage ) throws com.sun.star.uno.Exception
+ {
+ XTextRange cell = UnoRuntime.queryInterface( XTextRange.class,
+ m_document.getSheet( 0 ).getCellByPosition( col, row )
+ );
+ if ( !cell.getString().equals( text ) )
+ {
+ failed( failErrorMessage );
+ return false;
+ }
+ return true;
+ }
+
+ /* ------------------------------------------------------------------ */
+ /** sets the text of a given cell to a given string
+ */
+ private void setCellText( short col, short row, String text ) throws com.sun.star.uno.Exception
+ {
+ XTextRange cell = UnoRuntime.queryInterface( XTextRange.class,
+ m_document.getSheet( 0 ).getCellByPosition( col, row )
+ );
+ cell.setString( text );
+ }
+
+ /* ------------------------------------------------------------------ */
+ /** sets a numeric value in a given cell
+ */
+ private void setCellValue( short col, short row, double value ) throws com.sun.star.uno.Exception
+ {
+ XCell cell = UnoRuntime.queryInterface( XCell.class,
+ m_document.getSheet( 0 ).getCellByPosition( col, row )
+ );
+ cell.setValue( value );
+ }
+
+ /* ------------------------------------------------------------------ */
+ /** sets a numeric value in a given cell
+ */
+ private void setCellEmpty( short col, short row ) throws com.sun.star.uno.Exception
+ {
+ // as long as #i29130# is not fixed, we do not set the cell to "empty", but to
+ // an invalid form, which serves well for our purpose
+ XCellRangeFormula cell = UnoRuntime.queryInterface( XCellRangeFormula.class,
+ m_document.getSheet( 0 ).getCellByPosition( col, row )
+ );
+ String[][] args = new String[][] { new String[] { "=INVALID_FUNCTION()" } };
+ cell.setFormulaArray( args );
+ }
+
+ /* ------------------------------------------------------------------ */
+ /** binds the given control model to the given cell in the first sheet,
+ * using the given service name for the binding
+ */
+ private void bindToCell( XPropertySet controlModel, short column, short row, String _bindingServiceName ) throws com.sun.star.uno.Exception
+ {
+ XBindableValue bindableModel = UnoRuntime.queryInterface( XBindableValue.class,
+ controlModel
+ );
+
+ CellAddress address = new CellAddress();
+ address.Column = column;
+ address.Row = row;
+ address.Sheet = 0;
+
+ NamedValue[] parameters = new NamedValue[] { new NamedValue() };
+ parameters[0].Name = "BoundCell";
+ parameters[0].Value = address;
+
+ XValueBinding cellBinding = UnoRuntime.queryInterface( XValueBinding.class,
+ m_document.createInstanceWithArguments( _bindingServiceName, parameters )
+ );
+
+ bindableModel.setValueBinding( cellBinding );
+ }
+
+ /* ------------------------------------------------------------------ */
+ /** binds the given control model to the given cell in the first sheet
+ */
+ private void bindToCell( XPropertySet _controlModel, short _column, short _row ) throws com.sun.star.uno.Exception
+ {
+ bindToCell( _controlModel, _column, _row, "com.sun.star.table.CellValueBinding" );
+ }
+
+ /* ------------------------------------------------------------------ */
+ /** simulates a user action to check a radio button
+ */
+ private void simulateUserRadioCheck( XPropertySet radioModel ) throws com.sun.star.uno.Exception
+ {
+ XAccessible accessible = UnoRuntime.queryInterface(
+ XAccessible.class, m_document.getCurrentView().getControl( radioModel ) );
+
+ XAccessibleValue xValue = UnoRuntime.queryInterface(
+ XAccessibleValue.class, accessible.getAccessibleContext() );
+
+ Integer newValue = Integer.valueOf( 1 );
+ xValue.setCurrentValue( newValue );
+ }
+
+ /* ------------------------------------------------------------------ */
+ /** simulates a user action to check a radio button
+ */
+ private void simulateUserCheckBoxCheck( XPropertySet checkBox, short state ) throws com.sun.star.uno.Exception
+ {
+ XAccessible accessible = UnoRuntime.queryInterface(
+ XAccessible.class, m_document.getCurrentView().getControl( checkBox ) );
+
+ XAccessibleValue xValue = UnoRuntime.queryInterface(
+ XAccessibleValue.class, accessible.getAccessibleContext() );
+
+ xValue.setCurrentValue( Short.valueOf( state ) );
+ }
+
+ /* ------------------------------------------------------------------ */
+ /** simulates a user selecting an entry in a list box
+ */
+ private void simulateUserListBoxSelection( XPropertySet _listBox, String _selectEntry ) throws com.sun.star.uno.Exception
+ {
+ XListBox listBoxControl = UnoRuntime.queryInterface(
+ XListBox.class, m_document.getCurrentView().getControl( _listBox ) );
+ listBoxControl.selectItem( _selectEntry, true );
+ }
+
+ /* ------------------------------------------------------------------ */
+ /** simulates text input into the control belonging to the given model
+ */
+ private void simulateUserTextInput( XPropertySet controlModel, String text ) throws com.sun.star.uno.Exception
+ {
+ XAccessible accessible = UnoRuntime.queryInterface(
+ XAccessible.class, m_document.getCurrentView().getControl( controlModel ) );
+
+ UnoRuntime.queryInterface( XServiceInfo.class,
+ accessible.getAccessibleContext() );
+
+ XAccessibleEditableText textAccess = UnoRuntime.queryInterface(
+ XAccessibleEditableText.class, accessible.getAccessibleContext() );
+
+ textAccess.setText( text );
+ }
+}
diff --git a/forms/qa/integration/forms/ControlValidation.java b/forms/qa/integration/forms/ControlValidation.java
new file mode 100644
index 000000000..dce4a23f2
--- /dev/null
+++ b/forms/qa/integration/forms/ControlValidation.java
@@ -0,0 +1,176 @@
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+package integration.forms;
+
+import com.sun.star.uno.*;
+import com.sun.star.util.*;
+import com.sun.star.lang.*;
+import com.sun.star.beans.*;
+
+public class ControlValidation extends complexlib.ComplexTestCase implements com.sun.star.lang.XEventListener
+{
+ private DocumentHelper m_document; /// our current test document
+ private XMultiServiceFactory m_orb; /// our service factory
+
+ @Override
+ public String[] getTestMethodNames()
+ {
+ return new String[] {
+ "interactiveValidation"
+ };
+ }
+
+ @Override
+ public String getTestObjectName()
+ {
+ return "Form Control Validation Test";
+ }
+
+ public static boolean isInteractiveTest()
+ {
+ return true;
+ }
+
+ /* ------------------------------------------------------------------ */
+ /* test framework */
+ /* ------------------------------------------------------------------ */
+ public void before() throws java.lang.Exception
+ {
+ m_orb = param.getMSF();
+ }
+
+ /* ------------------------------------------------------------------ */
+ private void prepareTestStep( ) throws com.sun.star.uno.Exception, java.lang.Exception
+ {
+ m_document = DocumentHelper.blankTextDocument( m_orb );
+ m_document.getDocument( ).addEventListener( this );
+ }
+
+ /* ------------------------------------------------------------------ */
+ public void after()
+ {
+ closeDocument();
+ }
+
+ /* ------------------------------------------------------------------ */
+ /** closes our document, if we have an open one
+ */
+ private void closeDocument()
+ {
+ try
+ {
+ // close our document
+ if ( m_document != null )
+ {
+ XCloseable closeDoc = UnoRuntime.queryInterface( XCloseable.class,
+ m_document.getDocument() );
+ closeDoc.close( true );
+ }
+ }
+ catch ( com.sun.star.uno.Exception e )
+ {
+ e.printStackTrace( System.err );
+ }
+ }
+
+ /* ------------------------------------------------------------------ */
+ /* public test methods */
+ /* ------------------------------------------------------------------ */
+ public void interactiveValidation() throws com.sun.star.uno.Exception, java.lang.Exception
+ {
+ prepareTestStep();
+
+ SingleControlValidation validation;
+ XPropertySet focusField;
+
+ validation = new SingleControlValidation( m_document, 5, 5, "DatabaseFormattedField", new NumericValidator() );
+ focusField = validation.getInputField();
+ validation.setExplanatoryText( "Please enter a number between 0 and 100, with at most 1 decimal digit" );
+
+ validation = new SingleControlValidation( m_document, 90, 5, "DatabaseTextField", new TextValidator() );
+ validation.setExplanatoryText( "Please enter a text whose length is a multiple of 3, and which does not contain the letter 'Z'" );
+
+ validation = new SingleControlValidation( m_document, 5, 55, "DatabaseDateField", new DateValidator() );
+ validation.setExplanatoryText( "Please enter a date in the current month" );
+ validation.getInputField().setPropertyValue( "Dropdown", Boolean.TRUE );
+
+ validation = new SingleControlValidation( m_document, 90, 55, "DatabaseTimeField", new TimeValidator() );
+ validation.setExplanatoryText( "Please enter a time. Valid values are all full hours." );
+
+ validation = new SingleControlValidation( m_document, 5, 110, "DatabaseCheckBox", new BooleanValidator( false ) );
+ validation.setExplanatoryText( "Please check (well, or uncheck) the box. Don't leave it in indetermined state." );
+ validation.getInputField().setPropertyValue( "TriState", Boolean.TRUE );
+
+ validation = new SingleControlValidation( m_document, 90, 110, "DatabaseRadioButton", new BooleanValidator( true ), 3, 0 );
+ validation.setExplanatoryText( "Please check any but the first button" );
+
+ validation = new SingleControlValidation( m_document, 5, 165, "DatabaseListBox", new ListSelectionValidator( ), 1, 24 );
+ validation.setExplanatoryText( "Please select not more than two entries." );
+ validation.getInputField().setPropertyValue( "MultiSelection", Boolean.TRUE );
+ validation.getInputField().setPropertyValue( "StringItemList", new String[] { "first", "second", "third", "forth", "fivth" } );
+
+ // switch to alive mode
+ m_document.getCurrentView( ).toggleFormDesignMode( );
+ m_document.getCurrentView( ).grabControlFocus( focusField );
+
+ // wait for the user telling us to exit
+ waitForUserInput();
+ }
+
+ /* ------------------------------------------------------------------ */
+ /* internal methods */
+ /* ------------------------------------------------------------------ */
+ /** waits for the user to press a key (on the console where she started the java program)
+ or the document to be closed by the user.
+ @return
+ <TRUE/> if the user pressed a key on the console, <FALSE/> if she closed the document
+ */
+ protected boolean waitForUserInput() throws java.lang.Exception
+ {
+ synchronized (this)
+ {
+ integration.forms.WaitForInput aWait = new integration.forms.WaitForInput( this );
+ aWait.start();
+ wait();
+
+ // if the waiter thread is done, the user pressed enter
+ boolean bKeyPressed = aWait.isDone();
+ if ( !bKeyPressed )
+ aWait.interrupt();
+
+ return bKeyPressed;
+ }
+ }
+
+ /* ------------------------------------------------------------------ */
+ /* XEventListener overridables */
+ /* ------------------------------------------------------------------ */
+ public void disposing( com.sun.star.lang.EventObject eventObject )
+ {
+ if ( m_document.getDocument().equals( eventObject.Source ) )
+ {
+ // notify ourself that we can stop waiting for user input
+ synchronized (this)
+ {
+ notify();
+ }
+ }
+ }
+
+}
diff --git a/forms/qa/integration/forms/ControlValidator.java b/forms/qa/integration/forms/ControlValidator.java
new file mode 100644
index 000000000..fca013a19
--- /dev/null
+++ b/forms/qa/integration/forms/ControlValidator.java
@@ -0,0 +1,45 @@
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+package integration.forms;
+
+/** base class for components validating the content of form controls
+ */
+public abstract class ControlValidator implements com.sun.star.form.validation.XValidator
+{
+
+ public void addValidityConstraintListener(com.sun.star.form.validation.XValidityConstraintListener xValidityConstraintListener)
+ {
+ }
+
+ public void removeValidityConstraintListener(com.sun.star.form.validation.XValidityConstraintListener xValidityConstraintListener)
+ {
+ }
+
+ protected boolean isVoid( Object Value )
+ {
+ try
+ {
+ return ( com.sun.star.uno.AnyConverter.getType(Value).getTypeClass()
+ == com.sun.star.uno.TypeClass.VOID );
+ }
+ catch( java.lang.ClassCastException e )
+ {
+ }
+ return false;
+ }
+}
diff --git a/forms/qa/integration/forms/DateValidator.java b/forms/qa/integration/forms/DateValidator.java
new file mode 100644
index 000000000..255ec6f6f
--- /dev/null
+++ b/forms/qa/integration/forms/DateValidator.java
@@ -0,0 +1,80 @@
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+package integration.forms;
+
+public class DateValidator extends integration.forms.ControlValidator
+{
+
+ public String explainInvalid( Object Value )
+ {
+ try
+ {
+ if ( isVoid( Value ) )
+ return "empty input";
+
+ com.sun.star.util.Date dateValue = (com.sun.star.util.Date)Value;
+ if ( isDedicatedInvalidDate( dateValue ) )
+ return "this is no valid date";
+
+ if ( !isNextMonthsDate( dateValue ) )
+ return "date must denote a day in the current month";
+ }
+ catch( java.lang.Exception e )
+ {
+ return "oops. What did you enter for this to happen?";
+ }
+ return "";
+ }
+
+ public boolean isValid( Object Value )
+ {
+ try
+ {
+ if ( isVoid( Value ) )
+ return false;
+
+ com.sun.star.util.Date dateValue = (com.sun.star.util.Date)
+ com.sun.star.uno.AnyConverter.toObject(
+ com.sun.star.util.Date.class, Value);
+ if ( isDedicatedInvalidDate( dateValue ) )
+ return false;
+
+ return isNextMonthsDate( dateValue );
+ }
+ catch( java.lang.Exception e )
+ {
+ e.printStackTrace( System.err );
+ }
+ return false;
+ }
+
+ private boolean isDedicatedInvalidDate( com.sun.star.util.Date dateValue )
+ {
+ return ( dateValue.Day == 0 ) && ( dateValue.Month == 0 ) && ( dateValue.Year == 0 );
+ }
+
+ private boolean isNextMonthsDate( com.sun.star.util.Date dateValue )
+ {
+ java.util.Calendar today = java.util.Calendar.getInstance();
+ java.util.Calendar date = (java.util.Calendar) today.clone();
+ today.set(java.util.Calendar.DATE, 1);
+ date.set(dateValue.Year, dateValue.Month -1, 1); // Month value is 0-based. e.g., 0 for January.
+ date.add(java.util.Calendar.MONTH, -1);
+ return date.compareTo(today) == 0;
+ }
+}
diff --git a/forms/qa/integration/forms/DocumentHelper.java b/forms/qa/integration/forms/DocumentHelper.java
new file mode 100644
index 000000000..64caf0a15
--- /dev/null
+++ b/forms/qa/integration/forms/DocumentHelper.java
@@ -0,0 +1,375 @@
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+package integration.forms;
+
+import com.sun.star.beans.PropertyState;
+import com.sun.star.beans.PropertyValue;
+import com.sun.star.beans.XPropertySet;
+import com.sun.star.container.XIndexContainer;
+import com.sun.star.container.XNameContainer;
+import com.sun.star.document.MacroExecMode;
+import com.sun.star.drawing.XDrawPage;
+import com.sun.star.drawing.XDrawPageSupplier;
+import com.sun.star.drawing.XDrawPages;
+import com.sun.star.drawing.XDrawPagesSupplier;
+import com.sun.star.form.XFormsSupplier;
+import com.sun.star.frame.XComponentLoader;
+import com.sun.star.frame.XController;
+import com.sun.star.frame.XFrame;
+import com.sun.star.frame.XModel;
+import com.sun.star.lang.XComponent;
+import com.sun.star.lang.XMultiServiceFactory;
+import com.sun.star.lang.XServiceInfo;
+import com.sun.star.uno.UnoRuntime;
+import com.sun.star.uno.XInterface;
+import com.sun.star.util.XModifiable;
+
+/**************************************************************************/
+
+/**************************************************************************/
+/** provides a small wrapper around a document
+*/
+public class DocumentHelper
+{
+ private final XMultiServiceFactory m_orb;
+ private XComponent m_documentComponent;
+
+ /* ================================================================== */
+ /* ------------------------------------------------------------------ */
+ public DocumentHelper( XMultiServiceFactory orb, XComponent document )
+ {
+ m_orb = orb;
+ m_documentComponent = document;
+ }
+
+ /* ------------------------------------------------------------------ */
+ protected static XComponent implLoadAsComponent( XMultiServiceFactory orb, String documentOrFactoryURL ) throws com.sun.star.uno.Exception
+ {
+ return implLoadAsComponent( orb, documentOrFactoryURL, new PropertyValue[0] );
+ }
+
+ /* ------------------------------------------------------------------ */
+ private static XComponent implLoadAsComponent( XMultiServiceFactory orb, String documentOrFactoryURL, final PropertyValue[] i_args ) throws com.sun.star.uno.Exception
+ {
+ XComponentLoader aLoader = UnoRuntime.queryInterface(
+ XComponentLoader.class,
+ orb.createInstance( "com.sun.star.frame.Desktop" )
+ );
+
+ XComponent document = dbfTools.queryComponent(
+ aLoader.loadComponentFromURL( documentOrFactoryURL, "_blank", 0, i_args )
+ );
+ return document;
+ }
+
+ /* ------------------------------------------------------------------ */
+ private static DocumentHelper implLoadDocument( XMultiServiceFactory orb, String documentOrFactoryURL ) throws com.sun.star.uno.Exception
+ {
+ return implLoadDocument( orb, documentOrFactoryURL, new PropertyValue[0] );
+ }
+
+ /* ------------------------------------------------------------------ */
+ private static DocumentHelper implLoadDocument( XMultiServiceFactory orb, String documentOrFactoryURL, final PropertyValue[] i_args ) throws com.sun.star.uno.Exception
+ {
+ XComponent document = implLoadAsComponent( orb, documentOrFactoryURL, i_args );
+
+ XServiceInfo xSI = UnoRuntime.queryInterface( XServiceInfo.class,
+ document );
+ if ( xSI.supportsService( "com.sun.star.sheet.SpreadsheetDocument" ) )
+ return new SpreadsheetDocument( orb, document );
+ return new DocumentHelper( orb, document );
+ }
+
+ /* ------------------------------------------------------------------ */
+ public static DocumentHelper loadDocument( XMultiServiceFactory orb, String documentURL ) throws com.sun.star.uno.Exception
+ {
+ return implLoadDocument( orb, documentURL );
+ }
+
+ /* ------------------------------------------------------------------ */
+ public static DocumentHelper blankTextDocument( XMultiServiceFactory orb ) throws com.sun.star.uno.Exception
+ {
+ return blankDocument( orb, DocumentType.WRITER );
+ }
+
+
+
+ /* ------------------------------------------------------------------ */
+ public static DocumentHelper blankDocument( XMultiServiceFactory orb, DocumentType eType ) throws com.sun.star.uno.Exception
+ {
+ final PropertyValue[] args = new PropertyValue[] {
+ new PropertyValue( "MacroExecutionMode", -1, MacroExecMode.ALWAYS_EXECUTE, PropertyState.DIRECT_VALUE )
+ };
+ return implLoadDocument( orb, getDocumentFactoryURL( eType ), args );
+ }
+
+ /* ================================================================== */
+ /* ------------------------------------------------------------------ */
+ public XComponent getDocument( )
+ {
+ return m_documentComponent;
+ }
+
+ /* ------------------------------------------------------------------ */
+ public boolean isModified()
+ {
+ XModifiable modify = query( XModifiable.class );
+ return modify.isModified();
+ }
+
+ /* ------------------------------------------------------------------ */
+ public <T> T query( Class<T> aInterfaceClass )
+ {
+ return UnoRuntime.queryInterface( aInterfaceClass, m_documentComponent );
+ }
+
+ /* ------------------------------------------------------------------ */
+ public XMultiServiceFactory getOrb( )
+ {
+ return m_orb;
+ }
+
+ /* ------------------------------------------------------------------ */
+ /** retrieves the current view of the document
+ @return
+ the view component, queried for the interface described by aInterfaceClass
+ */
+ public DocumentViewHelper getCurrentView( )
+ {
+ // get the model interface for the document
+ XModel xDocModel = UnoRuntime.queryInterface(XModel.class, m_documentComponent );
+ // get the current controller for the document - as a controller is tied to a view,
+ // this gives us the currently active view for the document.
+ XController xController = xDocModel.getCurrentController();
+
+ if ( classify() == DocumentType.CALC )
+ return new SpreadsheetView( m_orb, this, xController );
+
+ return new DocumentViewHelper( m_orb, this, xController );
+ }
+
+ /* ------------------------------------------------------------------ */
+ /** reloads the document
+ *
+ * The reload is done by dispatching the respective URL at a frame of the document.
+ * As a consequence, if you have references to a view of the document, or any interface
+ * of the document, they will become invalid.
+ * The Model instance itself, at which you called reload, will still be valid, it will
+ * automatically update its internal state after the reload.
+ *
+ * Another consequence is that if the document does not have a view at all, it cannot
+ * be reloaded.
+ */
+ public void reload() throws Exception
+ {
+ DocumentViewHelper view = getCurrentView();
+ XFrame frame = view.getController().getFrame();
+ XModel oldModel = frame.getController().getModel();
+
+ getCurrentView().dispatch( ".uno:Reload" );
+
+ m_documentComponent = UnoRuntime.queryInterface( XComponent.class,
+ frame.getController().getModel() );
+
+ XModel newModel = getCurrentView().getController().getModel();
+ if ( UnoRuntime.areSame( oldModel, newModel ) )
+ throw new java.lang.IllegalStateException( "reload failed" );
+ }
+
+ /* ------------------------------------------------------------------ */
+ /** creates a new form which is a child of the given form components container
+
+ @param xParentContainer
+ The parent container for the new form
+ @param sInitialName
+ The initial name of the form. May be null, in this case the default (which
+ is an implementation detail) applies.
+ */
+ private XIndexContainer createSubForm( XIndexContainer xParentContainer, String sInitialName )
+ throws com.sun.star.uno.Exception
+ {
+ // create a new form
+ Object xNewForm = m_orb.createInstance( "com.sun.star.form.component.DataForm" );
+
+ // insert
+ xParentContainer.insertByIndex( xParentContainer.getCount(), xNewForm );
+
+ // set the name if necessary
+ if ( null != sInitialName )
+ {
+ XPropertySet xFormProps = dbfTools.queryPropertySet( xNewForm );
+ xFormProps.setPropertyValue( "Name", sInitialName );
+ }
+
+ // outta here
+ return UnoRuntime.queryInterface( XIndexContainer.class, xNewForm );
+ }
+
+ /* ------------------------------------------------------------------ */
+ /** creates a new form which is a child of the given form components container
+
+ @param aParentContainer
+ The parent container for the new form
+ @param sInitialName
+ The initial name of the form. May be null, in this case the default (which
+ is an implementation detail) applies.
+ */
+ public XIndexContainer createSubForm( Object aParentContainer, String sInitialName )
+ throws com.sun.star.uno.Exception
+ {
+ XIndexContainer xParentContainer = UnoRuntime.queryInterface(
+ XIndexContainer.class, aParentContainer );
+ return createSubForm( xParentContainer, sInitialName );
+ }
+
+ /* ------------------------------------------------------------------ */
+ /** creates a form which is a sibling of the given form
+ @param aForm
+ A sinbling of the to be created form.
+
+ @param sInitialName
+ The initial name of the form. May be null, in this case the default (which
+ is an implementation detail) applies.
+ */
+ public XIndexContainer createSiblingForm( Object aForm, String sInitialName ) throws com.sun.star.uno.Exception
+ {
+ // get the parent
+ XIndexContainer xContainer = (XIndexContainer)dbfTools.getParent(
+ aForm, XIndexContainer.class );
+ // append a new form to this parent container
+ return createSubForm( xContainer, sInitialName );
+ }
+
+
+
+ /* ------------------------------------------------------------------ */
+ /** returns a URL which can be used to create a document of a certain type
+ */
+ public static String getDocumentFactoryURL( DocumentType eType )
+ {
+ if ( eType == DocumentType.WRITER )
+ return "private:factory/swriter";
+ if ( eType == DocumentType.CALC )
+ return "private:factory/scalc";
+ if ( eType == DocumentType.DRAWING )
+ return "private:factory/sdraw";
+ if ( eType == DocumentType.XMLFORM )
+ return "private:factory/swriter?slot=21053";
+ return "private:factory/swriter";
+ }
+
+ /* ------------------------------------------------------------------ */
+ /** classifies a document
+ */
+ private DocumentType classify( )
+ {
+ XServiceInfo xSI = UnoRuntime.queryInterface(
+ XServiceInfo.class, m_documentComponent );
+
+ if ( xSI.supportsService( "com.sun.star.text.TextDocument" ) )
+ return DocumentType.WRITER;
+ else if ( xSI.supportsService( "com.sun.star.sheet.SpreadsheetDocument" ) )
+ return DocumentType.CALC;
+ else if ( xSI.supportsService( "com.sun.star.drawing.DrawingDocument" ) )
+ return DocumentType.DRAWING;
+
+ return DocumentType.UNKNOWN;
+ }
+
+ /* ------------------------------------------------------------------ */
+ /** retrieves a com.sun.star.drawing.DrawPage of the document, denoted by index
+ * @param index
+ * the index of the draw page
+ * @throws
+ * com.sun.star.lang.IndexOutOfBoundsException
+ * com.sun.star.lang.WrappedTargetException
+ */
+ protected XDrawPage getDrawPage( int index ) throws com.sun.star.lang.IndexOutOfBoundsException, com.sun.star.lang.WrappedTargetException
+ {
+ XDrawPagesSupplier xSuppPages = UnoRuntime.queryInterface(
+ XDrawPagesSupplier.class, getDocument() );
+ XDrawPages xPages = xSuppPages.getDrawPages();
+
+ return UnoRuntime.queryInterface( XDrawPage.class, xPages.getByIndex( index ) );
+ }
+
+ /* ------------------------------------------------------------------ */
+ /** retrieves the <type scope="com.sun.star.drawing">DrawPage</type> of the document
+ */
+ protected XDrawPage getMainDrawPage( ) throws com.sun.star.uno.Exception
+ {
+ XDrawPage xReturn;
+
+ // in case of a Writer document, this is rather easy: simply ask the XDrawPageSupplier
+ XDrawPageSupplier xSuppPage = UnoRuntime.queryInterface(
+ XDrawPageSupplier.class, getDocument() );
+ if ( null != xSuppPage )
+ xReturn = xSuppPage.getDrawPage();
+ else
+ { // the model itself is no draw page supplier - okay, it may be a Writer or Calc document
+ // (or any other multi-page document)
+ XDrawPagesSupplier xSuppPages = UnoRuntime.queryInterface(
+ XDrawPagesSupplier.class, getDocument() );
+ XDrawPages xPages = xSuppPages.getDrawPages();
+
+ xReturn = UnoRuntime.queryInterface( XDrawPage.class, xPages.getByIndex( 0 ) );
+
+ // Note that this is no 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
+ */
+ protected XNameContainer getFormComponentTreeRoot( ) throws com.sun.star.uno.Exception
+ {
+ XFormsSupplier xSuppForms = UnoRuntime.queryInterface(
+ XFormsSupplier.class, getMainDrawPage( ) );
+
+ XNameContainer xFormsCollection = null;
+ if ( null != xSuppForms )
+ {
+ xFormsCollection = xSuppForms.getForms();
+ }
+ return xFormsCollection;
+ }
+
+ /* ------------------------------------------------------------------ */
+ /** creates a component at the service factory provided by the document
+ */
+ public XInterface createInstance( String serviceSpecifier ) throws com.sun.star.uno.Exception
+ {
+ XMultiServiceFactory xORB = UnoRuntime.queryInterface( XMultiServiceFactory.class,
+ m_documentComponent );
+ return (XInterface)xORB.createInstance( serviceSpecifier );
+ }
+
+ /* ------------------------------------------------------------------ */
+ /** creates a component at the service factory provided by the document
+ */
+ public XInterface createInstanceWithArguments( String serviceSpecifier, Object[] arguments ) throws com.sun.star.uno.Exception
+ {
+ XMultiServiceFactory xORB = UnoRuntime.queryInterface( XMultiServiceFactory.class,
+ m_documentComponent );
+ return (XInterface) xORB.createInstanceWithArguments( serviceSpecifier, arguments );
+ }
+}
+
diff --git a/forms/qa/integration/forms/DocumentType.java b/forms/qa/integration/forms/DocumentType.java
new file mode 100644
index 000000000..105c31fc2
--- /dev/null
+++ b/forms/qa/integration/forms/DocumentType.java
@@ -0,0 +1,39 @@
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+package integration.forms;
+
+/** a helper "enumeration class" for classifying a document type
+*/
+public class DocumentType extends com.sun.star.uno.Enum
+{
+ private DocumentType( int value )
+ {
+ super( value );
+ }
+
+
+
+ public static final DocumentType WRITER = new DocumentType(0);
+ public static final DocumentType CALC = new DocumentType(1);
+ public static final DocumentType DRAWING = new DocumentType(2);
+ public static final DocumentType XMLFORM = new DocumentType(3);
+ public static final DocumentType UNKNOWN = new DocumentType(-1);
+
+
+}
+
diff --git a/forms/qa/integration/forms/DocumentViewHelper.java b/forms/qa/integration/forms/DocumentViewHelper.java
new file mode 100644
index 000000000..d936ea267
--- /dev/null
+++ b/forms/qa/integration/forms/DocumentViewHelper.java
@@ -0,0 +1,221 @@
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+package integration.forms;
+
+/**************************************************************************/
+
+import com.sun.star.awt.XControl;
+import com.sun.star.awt.XControlModel;
+import com.sun.star.awt.XWindow;
+import com.sun.star.awt.XToolkitExperimental;
+import com.sun.star.beans.PropertyValue;
+import com.sun.star.form.XForm;
+import com.sun.star.form.runtime.XFormController;
+import com.sun.star.frame.XController;
+import com.sun.star.frame.XDispatch;
+import com.sun.star.frame.XDispatchProvider;
+import com.sun.star.lang.XMultiServiceFactory;
+import com.sun.star.uno.UnoRuntime;
+import com.sun.star.util.URL;
+import com.sun.star.util.XURLTransformer;
+import com.sun.star.view.XControlAccess;
+import com.sun.star.view.XFormLayerAccess;
+import org.openoffice.xforms.XMLDocument;
+
+/**************************************************************************/
+/** provides a small wrapper around a document view
+*/
+public class DocumentViewHelper
+{
+ private final XMultiServiceFactory m_orb;
+ private final XController m_controller;
+ private final DocumentHelper m_document;
+
+ /* ------------------------------------------------------------------ */
+ final protected XController getController()
+ {
+ return m_controller;
+ }
+
+ /* ------------------------------------------------------------------ */
+ final protected DocumentHelper getDocument()
+ {
+ return m_document;
+ }
+
+ /* ------------------------------------------------------------------ */
+ public DocumentViewHelper( XMultiServiceFactory orb, DocumentHelper document, XController controller )
+ {
+ m_orb = orb;
+ m_document = document;
+ m_controller = controller;
+ }
+
+ /* ------------------------------------------------------------------ */
+ /** Quick access to a given interface of the view
+ @param aInterfaceClass
+ the class of the interface which shall be returned
+ */
+ public <T> T query( Class<T> aInterfaceClass )
+ {
+ return UnoRuntime.queryInterface( aInterfaceClass, m_controller );
+ }
+
+ /* ------------------------------------------------------------------ */
+ /** retrieves a dispatcher for the given URL, obtained at the current view of the document
+ @param aURL
+ a one-element array. The first element must contain a valid
+ <member scope="com.sun.star.util">URL::Complete</member> value. Upon return, the URL is correctly
+ parsed.
+ @return
+ the dispatcher for the URL in question
+ */
+ private XDispatch getDispatcher( URL[] aURL ) throws java.lang.Exception
+ {
+ XDispatch xReturn = null;
+
+ // go get the current view
+ XController xController = query( XController.class );
+ // go get the dispatch provider of its frame
+ XDispatchProvider xProvider = UnoRuntime.queryInterface(
+ XDispatchProvider.class, xController.getFrame() );
+ if ( null != xProvider )
+ {
+ // need a URLTransformer
+ XURLTransformer xTransformer = UnoRuntime.queryInterface(
+ XURLTransformer.class, m_orb.createInstance( "com.sun.star.util.URLTransformer" ) );
+ xTransformer.parseStrict( aURL );
+
+ xReturn = xProvider.queryDispatch( aURL[0], "", 0 );
+ }
+ return xReturn;
+ }
+
+ /* ------------------------------------------------------------------ */
+ /** retrieves a dispatcher for the given URL, obtained at the current view of the document
+ */
+ public XDispatch getDispatcher( String url ) throws java.lang.Exception
+ {
+ URL[] aURL = new URL[] { new URL() };
+ aURL[0].Complete = url;
+ return getDispatcher( aURL );
+ }
+
+ /* ------------------------------------------------------------------ */
+ /** dispatches the given URL into the view, if there's a dispatcher for it
+
+ @return
+ <TRUE/> if the URL was successfully dispatched
+ */
+ public boolean dispatch( String url ) throws java.lang.Exception
+ {
+ URL[] completeURL = new URL[] { new URL() };
+ completeURL[0].Complete = url;
+ XDispatch dispatcher = getDispatcher( completeURL );
+ if ( dispatcher == null )
+ return false;
+
+ PropertyValue[] aDummyArgs = new PropertyValue[] { };
+ dispatcher.dispatch( completeURL[0], aDummyArgs );
+ return true;
+ }
+
+ /* ------------------------------------------------------------------ */
+ /** retrieves a control within the current view of a document
+ @param xModel
+ specifies the control model whose control should be located
+ @return
+ the control tied to the model
+ */
+ public XControl getControl( XControlModel xModel ) throws com.sun.star.uno.Exception
+ {
+ // the current view of the document
+ XControlAccess xCtrlAcc = query( XControlAccess.class );
+ // delegate the task of looking for the control
+ return xCtrlAcc.getControl( xModel );
+ }
+
+ /* ------------------------------------------------------------------ */
+ public XControl getControl( Object aModel ) throws com.sun.star.uno.Exception
+ {
+ XControlModel xModel = UnoRuntime.queryInterface( XControlModel.class, aModel );
+ return getControl( xModel );
+ }
+
+ /* ------------------------------------------------------------------ */
+ public <T> T getControl( Object aModel, Class<T> aInterfaceClass ) throws com.sun.star.uno.Exception
+ {
+ XControlModel xModel = UnoRuntime.queryInterface( XControlModel.class, aModel );
+ return UnoRuntime.queryInterface( aInterfaceClass, getControl( xModel ) );
+ }
+
+ /* ------------------------------------------------------------------ */
+ /** retrieves the form controller for a given logical form
+ */
+ private XFormController getFormController( XForm _form )
+ {
+ XFormLayerAccess formLayerAccess = query( XFormLayerAccess.class );
+ return formLayerAccess.getFormController( _form );
+ }
+
+ /* ------------------------------------------------------------------ */
+ /** retrieves the form controller for a given logical form
+ */
+ public XFormController getFormController( Object _form )
+ {
+ return getFormController( UnoRuntime.queryInterface( XForm.class, _form ));
+ }
+
+ /* ------------------------------------------------------------------ */
+ /** toggles the design mode of the form layer of active view of our sample document
+ */
+ protected void toggleFormDesignMode( ) throws java.lang.Exception
+ {
+ if ( m_document instanceof XMLDocument )
+ dispatch( ".uno:SwitchXFormsDesignMode" );
+ else
+ dispatch( ".uno:SwitchControlDesignMode" );
+ // at least SwitchControlDesignMode is async, so wait for it to be done
+ XToolkitExperimental xToolkit = UnoRuntime.queryInterface(
+ XToolkitExperimental.class,
+ m_orb.createInstance("com.sun.star.awt.Toolkit"));
+ xToolkit.processEventsToIdle();
+ }
+
+ /* ------------------------------------------------------------------ */
+ /** 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 = UnoRuntime.queryInterface( XWindow.class,
+ xControl );
+
+ // grab the focus
+ xControlWindow.setFocus();
+ }
+
+
+}
+
diff --git a/forms/qa/integration/forms/FormComponent.java b/forms/qa/integration/forms/FormComponent.java
new file mode 100644
index 000000000..1861c084e
--- /dev/null
+++ b/forms/qa/integration/forms/FormComponent.java
@@ -0,0 +1,114 @@
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+package integration.forms;
+
+import com.sun.star.uno.UnoRuntime;
+import com.sun.star.form.XFormsSupplier;
+import com.sun.star.container.XNameAccess;
+import com.sun.star.container.XIndexAccess;
+import com.sun.star.container.XChild;
+import com.sun.star.container.XNamed;
+import com.sun.star.drawing.XDrawPage;
+
+public class FormComponent
+{
+ private final Object m_component;
+ private final XNameAccess m_nameAccess;
+ private final XIndexAccess m_indexAccess;
+
+ /* ------------------------------------------------------------------ */
+ private FormComponent()
+ {
+ m_component = null;
+ m_nameAccess = null;
+ m_indexAccess = null;
+ }
+
+ /* ------------------------------------------------------------------ */
+ public FormComponent( XDrawPage drawPage )
+ {
+ XFormsSupplier supp = UnoRuntime.queryInterface(
+ XFormsSupplier.class, drawPage );
+ m_component = supp.getForms();
+
+ m_nameAccess = (XNameAccess)m_component;
+ m_indexAccess = UnoRuntime.queryInterface(
+ XIndexAccess.class, m_component );
+ UnoRuntime.queryInterface(
+ XChild.class, m_component );
+ UnoRuntime.queryInterface(
+ XNamed.class, m_component );
+ }
+
+ /* ------------------------------------------------------------------ */
+ private FormComponent( Object element )
+ {
+ m_component = element;
+ m_nameAccess = UnoRuntime.queryInterface(
+ XNameAccess.class, m_component );
+ m_indexAccess = UnoRuntime.queryInterface(
+ XIndexAccess.class, m_component );
+ UnoRuntime.queryInterface(
+ XChild.class, m_component );
+ UnoRuntime.queryInterface(
+ XNamed.class, m_component );
+ }
+
+ /* ------------------------------------------------------------------ */
+ /** Quick access to a given interface of the view
+ @param aInterfaceClass
+ the class of the interface which shall be returned
+ */
+ public <T> T query( Class<T> aInterfaceClass )
+ {
+ return UnoRuntime.queryInterface( aInterfaceClass, m_component );
+ }
+
+ /* ------------------------------------------------------------------ */
+ public FormComponent getByName( String name )
+ {
+ try
+ {
+ if ( m_nameAccess != null )
+ return new FormComponent( m_nameAccess.getByName( name ) );
+ }
+ catch( com.sun.star.uno.Exception e )
+ {
+ System.err.println( e );
+ e.printStackTrace( System.err );
+ }
+ return new FormComponent();
+ }
+
+ /* ------------------------------------------------------------------ */
+ public FormComponent getByIndex( int index )
+ {
+ try
+ {
+ if ( m_indexAccess != null )
+ return new FormComponent( m_indexAccess.getByIndex( index ) );
+ }
+ catch( com.sun.star.uno.Exception e )
+ {
+ System.err.println( e );
+ e.printStackTrace( System.err );
+ }
+ return new FormComponent();
+ }
+
+}
diff --git a/forms/qa/integration/forms/FormControlTest.java b/forms/qa/integration/forms/FormControlTest.java
new file mode 100644
index 000000000..773a0197a
--- /dev/null
+++ b/forms/qa/integration/forms/FormControlTest.java
@@ -0,0 +1,951 @@
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+package integration.forms;
+
+import com.sun.star.awt.XImageProducer;
+import com.sun.star.beans.PropertyValue;
+import com.sun.star.beans.XPropertySet;
+import com.sun.star.container.XNameAccess;
+import com.sun.star.form.runtime.XFormController;
+import com.sun.star.form.XImageProducerSupplier;
+import com.sun.star.frame.XDispatch;
+import com.sun.star.lang.EventObject;
+import com.sun.star.lang.XMultiServiceFactory;
+import com.sun.star.sdb.CommandType;
+import com.sun.star.sdb.SQLErrorEvent;
+import com.sun.star.sdb.XSQLErrorBroadcaster;
+import com.sun.star.sdb.XSQLErrorListener;
+import com.sun.star.sdbc.XDataSource;
+import com.sun.star.sdbc.XResultSet;
+import com.sun.star.sdbc.XResultSetUpdate;
+import com.sun.star.uno.UnoRuntime;
+import com.sun.star.uno.XNamingService;
+import com.sun.star.util.URL;
+import com.sun.star.util.XCloseable;
+import com.sun.star.util.XURLTransformer;
+import connectivity.tools.HsqlDatabase;
+import connectivity.tools.sdb.Connection;
+import java.io.FileOutputStream;
+
+
+public class FormControlTest extends complexlib.ComplexTestCase implements XSQLErrorListener
+{
+ private static String s_tableName = "CTC_form_controls";
+
+ private HsqlDatabase m_databaseDocument;
+ private XDataSource m_dataSource;
+ private XPropertySet m_dataSourceProps;
+ private XMultiServiceFactory m_orb;
+ private DocumentHelper m_document;
+ private FormLayer m_formLayer;
+ private XPropertySet m_masterForm;
+ private String m_sImageURL;
+ private SQLErrorEvent m_mostRecentErrorEvent;
+
+ private static final String m_dataSourceName = "integration.forms.FormControlTest";
+
+ /* ------------------------------------------------------------------ */
+ @Override
+ public String[] getTestMethodNames()
+ {
+ return new String[] {
+ "checkFirstRow",
+ "checkInsertRow",
+ "checkImageControl",
+ "checkCrossUpdates_checkBox",
+ "checkCrossUpdates_radioButton",
+ "checkRowUpdates",
+ "checkEmptyIsNull"
+ };
+ }
+
+ /* ------------------------------------------------------------------ */
+ @Override
+ public String getTestObjectName()
+ {
+ return "Database Form Controls Test";
+ }
+
+ /* ------------------------------------------------------------------ */
+ /// pre-test initialization
+ public void before() throws com.sun.star.uno.Exception, java.lang.Exception
+ {
+ // ensure that we have a data source to work with, and the required tables
+ if ( !ensureDataSource() || !ensureTables() )
+ {
+ failed( "could not access the required data source or table therein." );
+ return;
+ }
+
+ // create the document which we work on
+ createSampleDocument();
+
+ createImageFile();
+ }
+
+ /* ------------------------------------------------------------------ */
+ public void checkFirstRow() throws com.sun.star.uno.Exception, java.lang.Exception
+ {
+ moveToFirst();
+
+ // and check the content of the various controls
+ if ( !checkRadios( (short)1, (short)0, (short)0 )
+ || !checkDoubleValue( 1, "ID", "Value" )
+ || !checkDoubleValue( 42, "f_integer", "EffectiveValue" )
+ || !checkStringValue( "the answer", "f_text", "Text" )
+ || !checkDoubleValue( 0.12, "f_decimal", "Value" )
+ || !checkIntValue ( 20030922, "f_date", "Date" )
+ || !checkIntValue ( 15000000, "f_time", "Time" )
+ || !checkIntValue ( 20030923, "f_timestamp_date", "Date" )
+ || !checkIntValue ( 17152300, "f_timestamp_time", "Time" )
+ || !checkShortValue ( (short)1, "f_tinyint", "State" )
+ )
+ {
+ failed( "checking the content of one or more controls on the first row failed (see the log for details)" );
+ return;
+ }
+ }
+
+ /* ------------------------------------------------------------------ */
+ public void checkInsertRow() throws com.sun.star.uno.Exception, java.lang.Exception
+ {
+ // move the cursor to the insert row
+ moveToInsertRow();
+
+ // and check the content of the various controls
+ if ( !verifyCleanInsertRow() )
+ {
+ failed( "checking the content of one or more controls on the insert row failed (see the log for details)" );
+ return;
+ }
+ }
+
+ /* ------------------------------------------------------------------ */
+ /// some tests with the image control
+ public void checkImageControl() throws com.sun.star.uno.Exception, java.lang.Exception
+ {
+ // since we did not yet insert any image, the control should not display one ...
+ moveToFirst();
+ if ( !verifyReferenceImage( new byte[0] ) )
+ {
+ failed( "image control failed to display empty image" );
+ return;
+ }
+
+ // check if the image control is able to insert our sample image into the database
+ // insert an
+ XPropertySet xImageModel = getControlModel( "f_blob" );
+ xImageModel.setPropertyValue( "ImageURL", m_sImageURL );
+
+ if ( !verifyReferenceImage( getSamplePictureBytes() ) )
+ {
+ failed( "image control does not display the sample image as required" );
+ return;
+ }
+
+ // save the record
+ saveRecordByUI();
+
+ // still needs to be the sample image
+ if ( !verifyReferenceImage( getSamplePictureBytes() ) )
+ {
+ failed( "image control does not, after saving the record, display the sample image as required" );
+ return;
+ }
+
+ // on the next record, the image should be empty
+ moveToNext();
+ if ( !verifyReferenceImage( new byte[0] ) )
+ {
+ failed( "image control failed to display empty image, after coming from a non-empty image" );
+ return;
+ }
+
+ // back to the record where we just inserted the image, it should be our sample image
+ moveToFirst();
+ if ( !verifyReferenceImage( getSamplePictureBytes() ) )
+ {
+ failed( "image control does not, after coming back to the record, display the sample image as required" );
+ return;
+ }
+
+ // okay, now remove the image
+ xImageModel.setPropertyValue( "ImageURL", "" );
+ if ( !verifyReferenceImage( new byte[0] ) )
+ {
+ failed( "image control failed to remove the image" );
+ return;
+ }
+ nextRecordByUI();
+ previousRecordByUI();
+ if ( !verifyReferenceImage( new byte[0] ) )
+ {
+ failed( "image still there after coming back, though we just removed it" );
+ return;
+ }
+ }
+
+ /* ------------------------------------------------------------------ */
+ /** This is both a test for controls which are bound to the same column (they must reflect
+ * each others updates), and for the immediate updates which need to happen for both check
+ * boxes and radio buttons: They must commit their content to the underlying column as soon
+ * as the change is made, *not* only upon explicit commit
+ */
+ public void checkCrossUpdates_checkBox() throws com.sun.star.uno.Exception, java.lang.Exception
+ {
+ // move to the first record
+ moveToFirst();
+ if ( !checkShortValue ( (short)1, "f_tinyint", "State" )
+ || !checkDoubleValue( 1, "f_tinyint_format", "EffectiveValue" )
+ )
+ {
+ failed( "huh? inconsistence in the test!" );
+ // we created the sample data in a way that the f_tinyint field should contain a "1" at the first
+ // record. We already asserted the proper function of the check box in checkFirstRow, so if this
+ // fails here, the script became inconsistent
+ return;
+ }
+
+ XPropertySet checkModel = getControlModel( "f_tinyint" );
+ checkModel.setPropertyValue( "State", Short.valueOf( (short)0 ) );
+
+ // setting the state of the check box needs to be reflected in the formatted field immediately
+ if ( !checkDoubleValue( 0, "f_tinyint_format", "EffectiveValue" ) )
+ {
+ failed( "cross-update failed: updating the check box should result in updating the same-bound formatted field (1)!" );
+ return;
+ }
+
+ // same for the "indetermined" state of the check box
+ checkModel.setPropertyValue( "State", Short.valueOf( (short)2 ) );
+ if ( !checkNullValue( "f_tinyint_format", "EffectiveValue" ) )
+ {
+ failed( "cross-update failed: updating the check box should result in updating the same-bound formatted field (2)!" );
+ return;
+ }
+
+ // undo the changes done so far
+ undoRecordByUI();
+ // and see if this is properly reflected in the controls
+ if ( !checkShortValue ( (short)1, "f_tinyint", "State" )
+ || !checkDoubleValue( 1, "f_tinyint_format", "EffectiveValue" )
+ )
+ {
+ failed( "either the check box or the formatted field failed to recognize the UNDO!" );
+ return;
+ }
+
+ // the other way round - when changing the formatted field - the change should *not*
+ // be reflected to the check box, since the formatted field needs an explicit commit
+ XPropertySet tinyFormattedModel = getControlModel( "f_tinyint_format" );
+ m_document.getCurrentView().grabControlFocus( tinyFormattedModel );
+ m_formLayer.userTextInput( tinyFormattedModel, "0" );
+ if ( !checkShortValue ( (short)1, "f_tinyint", "State" )
+ )
+ {
+ failed( "the check box should not be updated here! (did the formatted model commit immediately?)" );
+ return;
+ }
+
+ // set the focus to *any* other control (since we just have it at hand, we use the check box control)
+ // this should result in the formatted control being committed, and thus in the check box updating
+ m_document.getCurrentView().grabControlFocus( checkModel );
+ if ( !checkShortValue ( (short)0, "f_tinyint", "State" )
+ )
+ {
+ failed( "formatted field did not commit (or check box did not update)" );
+ return;
+ }
+
+ // undo the changes done so far, so we leave the document in a clean state for the next test
+ undoRecordByUI();
+ }
+
+ /* ------------------------------------------------------------------ */
+ /** very similar to checkCrossUpdates_checkBox - does nearly the same for the radio buttons. See there for more
+ * explanations.
+ */
+ public void checkCrossUpdates_radioButton() throws com.sun.star.uno.Exception, java.lang.Exception
+ {
+ // move to the first record
+ moveToFirst();
+ if ( !checkRadios( (short)1, (short)0, (short)0 )
+ || !checkStringValue( "none", "f_text_enum_text", "Text" )
+ )
+ {
+ failed( "huh? inconsistence in the test!" );
+ return;
+ }
+
+ XPropertySet radioModel = getRadioModel( "radio_group", "normal" );
+ radioModel.setPropertyValue( "State", Short.valueOf( (short)1 ) );
+
+ // setting the state of the radio button needs to be reflected in the formatted field immediately
+ if ( !checkStringValue( "normal", "f_text_enum_text", "Text" ) )
+ {
+ failed( "cross-update failed: updating the radio button should result in updating the same-bound text field (1)!" );
+ return;
+ }
+
+ // same for the "indetermined" state of the check box
+ getRadioModel( "radio_group", "important" ).setPropertyValue( "State", Short.valueOf( (short)1 ) );
+ if ( !checkStringValue( "important", "f_text_enum_text", "Text" ) )
+ {
+ failed( "cross-update failed: updating the radio button should result in updating the same-bound text field (2)!" );
+ return;
+ }
+
+ // undo the changes done so far
+ undoRecordByUI();
+ // and see if this is properly reflected in the controls
+ if ( !checkRadios( (short)1, (short)0, (short)0 )
+ || !checkStringValue( "none", "f_text_enum_text", "Text" )
+ )
+ {
+ failed( "either the radio button or the text field failed to recognize the UNDO!" );
+ return;
+ }
+
+ // the other way round - when changing the formatted field - the change should *not*
+ // be reflected to the check box, since the formatted field needs an explicit commit
+ XPropertySet textModel = getControlModel( "f_text_enum_text" );
+ m_document.getCurrentView().grabControlFocus( textModel );
+ m_formLayer.userTextInput( textModel, "normal" );
+ if ( !checkRadios( (short)1, (short)0, (short)0 )
+ )
+ {
+ failed( "the radio buttons should not be updated here! (did the formatted model commit immediately?)" );
+ return;
+ }
+
+ // set the focus to *any* other control (since we just have it at hand, we use the check box control)
+ // this should result in the formatted control being committed, and thus in the check box updating
+ m_document.getCurrentView().grabControlFocus( radioModel );
+ if ( !checkRadios( (short)0, (short)1, (short)0 )
+ )
+ {
+ failed( "text field did not commit (or radio button did not update)" );
+ return;
+ }
+
+ // undo the changes done so far, so we leave the document in a clean state for the next test
+ undoRecordByUI();
+ }
+
+ /* ------------------------------------------------------------------ */
+ /** some tests with updating the table via our controls
+ */
+ public void checkRowUpdates() throws com.sun.star.uno.Exception, java.lang.Exception
+ {
+ // start with inserting a new record
+ moveToInsertRow();
+ assure( "insert row not in expected clean state", verifyCleanInsertRow() );
+
+ userTextInput( "ID", "3", true );
+ userTextInput( "f_integer", "729", true );
+ userTextInput( "f_text", "test", true );
+ userTextInput( "f_decimal", "152343", true );
+ userTextInput( "f_date", "31.12.1999", true );
+ userTextInput( "f_time", "23:59:59", true );
+
+ // move to the next row, this should automatically commit the changes we made
+ nextRecordByUI();
+ // and back to the row we just inserted
+ previousRecordByUI();
+
+ if ( !checkDoubleValue( 3, "ID", "Value" )
+ || !checkDoubleValue( 729, "f_integer", "EffectiveValue" )
+ || !checkStringValue( "test", "f_text", "Text" )
+ || !checkDoubleValue( 152343, "f_decimal", "Value" )
+ || !checkIntValue ( 19991231, "f_date", "Date" )
+ || !checkIntValue ( 23595900, "f_time", "Time" )
+ )
+ {
+ failed( "the changes we made on the insert row have not been committed" );
+ return;
+ }
+
+ // now change the data, to see if regular updates work, too
+ userTextInput( "ID", "4", true );
+ userTextInput( "f_integer", "618", true );
+ userTextInput( "f_text", "yet another stupid, meaningless text", true );
+ userTextInput( "f_required_text", "this must not be NULL", true );
+ userTextInput( "f_decimal", "4562", true );
+ userTextInput( "f_date", "26.03.2004", true );
+ userTextInput( "f_time", "17:05:00", true );
+
+ // move to the next row, this should automatically commit the changes we made
+ nextRecordByUI();
+ // and back to the row we just inserted
+ previousRecordByUI();
+
+ if ( !checkDoubleValue( 4, "ID", "Value" )
+ || !checkDoubleValue( 618, "f_integer", "EffectiveValue" )
+ || !checkStringValue( "yet another stupid, meaningless text", "f_text", "Text" )
+ || !checkDoubleValue( 4562, "f_decimal", "Value" )
+ || !checkIntValue ( 20040326, "f_date", "Date" )
+ || !checkIntValue ( 17050000, "f_time", "Time" )
+ )
+ {
+ failed( "the changes we made on the insert row have not been committed" );
+ return;
+ }
+
+ m_document.getCurrentView().grabControlFocus( getControlModel( "ID" ) );
+ }
+
+ /* ------------------------------------------------------------------ */
+ /** checks the "ConvertEmptyToNull" property behavior of an edit control
+ *
+ */
+ public void checkEmptyIsNull() throws com.sun.star.uno.Exception, java.lang.Exception
+ {
+ // start with inserting a new record
+ moveToInsertRow();
+ assure( "insert row not in expected clean state", verifyCleanInsertRow() );
+
+ // make an input in any field, but leave the edit control which is bound to a required field
+ // empty
+ userTextInput( "ID", "5", true );
+ userTextInput( "f_text", "more text", true );
+
+ // this should *not* fail. Even if we did not input anything into the control bound to the
+ // f_required_text column, this control's reset (done when moving to the insertion row) is
+ // expected to write an empty string into its bound column, since its EmptyIsNULL property
+ // is set to FALSE
+ // (#i92471#)
+ m_mostRecentErrorEvent = null;
+ nextRecordByUI();
+ assure( "updating an incomplete record did not work as expected", m_mostRecentErrorEvent == null );
+ }
+
+ /* ------------------------------------------------------------------ */
+ private boolean verifyCleanInsertRow( ) throws com.sun.star.uno.Exception, java.lang.Exception
+ {
+ // and check the content of the various controls
+ return ( checkRadios( (short)0, (short)0, (short)0 )
+ && checkShortValue( (short)2, "f_tinyint", "State" )
+ && checkStringValue( "", "f_text", "Text" )
+ && checkNullValue( "ID", "Value" )
+ && checkNullValue( "f_integer", "EffectiveValue" )
+ && checkNullValue( "f_decimal", "Value" )
+ );
+ }
+
+ /* ------------------------------------------------------------------ */
+ /// post-test cleanup
+ public void after() throws com.sun.star.uno.Exception, java.lang.Exception
+ {
+ // close our document
+ if ( m_document != null )
+ {
+ XCloseable closeDoc = UnoRuntime.queryInterface( XCloseable.class,
+ m_document.getDocument() );
+ closeDoc.close( true );
+ }
+ }
+
+
+ /* ------------------------------------------------------------------ */
+ private boolean ensureDataSource() throws Exception
+ {
+ m_orb = param.getMSF();
+
+ XNameAccess databaseContext = UnoRuntime.queryInterface( XNameAccess.class,
+ m_orb.createInstance( "com.sun.star.sdb.DatabaseContext" ) );
+ XNamingService namingService = UnoRuntime.queryInterface( XNamingService.class,
+ databaseContext );
+
+ // revoke the data source, if it previously existed
+ if ( databaseContext.hasByName( m_dataSourceName ) )
+ namingService.revokeObject( m_dataSourceName );
+
+ // create a new ODB file, and register it with its URL
+ m_databaseDocument = new HsqlDatabase( m_orb );
+ String documentURL = m_databaseDocument.getDocumentURL();
+ namingService.registerObject( m_dataSourceName, databaseContext.getByName( documentURL ) );
+
+ m_dataSource = UnoRuntime.queryInterface( XDataSource.class,
+ databaseContext.getByName( m_dataSourceName ) );
+ m_dataSourceProps = dbfTools.queryPropertySet( m_dataSource );
+
+ XPropertySet dataSourceSettings = UnoRuntime.queryInterface( XPropertySet.class,
+ m_dataSourceProps.getPropertyValue( "Settings" ) );
+ dataSourceSettings.setPropertyValue( "FormsCheckRequiredFields", Boolean.FALSE );
+
+ return m_dataSource != null;
+ }
+
+ /* ------------------------------------------------------------------ */
+ /** retrieves the control model with the given name
+ */
+ private XPropertySet getControlModel( String name ) throws com.sun.star.uno.Exception, java.lang.Exception
+ {
+ XNameAccess nameAccess = UnoRuntime.queryInterface( XNameAccess.class,
+ m_masterForm );
+ return UnoRuntime.queryInterface( XPropertySet.class,
+ nameAccess.getByName( name ) );
+ }
+
+ /* ------------------------------------------------------------------ */
+ private void createSampleDocument() throws com.sun.star.uno.Exception, java.lang.Exception
+ {
+
+ m_document = DocumentHelper.blankTextDocument( m_orb );
+ m_formLayer = new FormLayer( m_document );
+
+ // insert some controls
+ XPropertySet xIDField = m_formLayer.insertControlLine( "DatabaseNumericField", "ID", "", 3 );
+ m_formLayer.insertControlLine( "DatabaseFormattedField","f_integer", "", 11 );
+ m_formLayer.insertControlLine( "DatabaseTextField", "f_text", "", 19 );
+ XPropertySet xReqField = m_formLayer.insertControlLine( "DatabaseTextField", "f_required_text", "", 27 );
+ m_formLayer.insertControlLine( "DatabaseNumericField", "f_decimal", "", 35 );
+ m_formLayer.insertControlLine( "DatabaseDateField", "f_date", "", 43 );
+ XPropertySet xTimeField = m_formLayer.insertControlLine( "DatabaseTimeField", "f_time", "", 51 );
+ m_formLayer.insertControlLine( "DatabaseDateField", "f_timestamp", "_date", 59 );
+ m_formLayer.insertControlLine( "DatabaseTimeField", "f_timestamp", "_time", 67 );
+ XPropertySet xImageField = m_formLayer.insertControlLine( "DatabaseImageControl", "f_blob", "", 2, 75, 40 );
+ m_formLayer.insertControlLine( "DatabaseTextField", "f_text_enum", "_text", 80, 25, 6 );
+ XPropertySet xCheckBox = m_formLayer.insertControlLine( "DatabaseCheckBox", "f_tinyint", "", 80, 33, 6 );
+ m_formLayer.insertControlLine( "DatabaseFormattedField","f_tinyint", "_format",80, 41, 6 );
+ m_formLayer.insertControlLine( "DatabaseTextField", "dummy", "", 150 );
+
+ xIDField.setPropertyValue( "DecimalAccuracy", Short.valueOf( (short)0 ) );
+ xImageField.setPropertyValue( "ScaleImage", Boolean.TRUE );
+ xImageField.setPropertyValue( "Tabstop", Boolean.TRUE );
+ xCheckBox.setPropertyValue( "TriState", Boolean.TRUE );
+ xCheckBox.setPropertyValue( "DefaultState", Short.valueOf( (short)2 ) );
+ xTimeField.setPropertyValue( "TimeFormat", Short.valueOf( (short)1 ) );
+ xTimeField.setPropertyValue( "TimeMax", Integer.valueOf( 23595999 ) );
+ xReqField.setPropertyValue( "ConvertEmptyToNull", Boolean.FALSE );
+
+ // the logical form
+ m_masterForm = (XPropertySet)dbfTools.getParent( xIDField, XPropertySet.class );
+ m_masterForm.setPropertyValue( "DataSourceName", m_dataSourceProps.getPropertyValue( "Name" ) );
+ m_masterForm.setPropertyValue( "CommandType", Integer.valueOf( CommandType.TABLE ) );
+ m_masterForm.setPropertyValue( "Command", s_tableName );
+
+ insertRadio( 3, "none", "none" );
+ insertRadio( 10, "normal", "normal" );
+ insertRadio( 17, "important", "important" );
+
+ // switch the forms into data entry mode
+ m_document.getCurrentView( ).toggleFormDesignMode( );
+
+ XFormController masterFormController = m_document.getCurrentView().getFormController( m_masterForm );
+ XSQLErrorBroadcaster errorBroadcaster = UnoRuntime.queryInterface( XSQLErrorBroadcaster.class,
+ masterFormController );
+ errorBroadcaster.addSQLErrorListener( this );
+
+ // set the focus to the ID control
+ m_document.getCurrentView().grabControlFocus( xIDField );
+ }
+
+ /* ------------------------------------------------------------------ */
+ private void insertRadio( int nYPos, String label, String refValue ) throws com.sun.star.uno.Exception, java.lang.Exception
+ {
+ XPropertySet xRadio = m_formLayer.createControlAndShape( "DatabaseRadioButton", 106, nYPos, 25, 6 );
+ xRadio.setPropertyValue( "Label", label );
+ xRadio.setPropertyValue( "RefValue", refValue );
+ xRadio.setPropertyValue( "Name", "radio_group" );
+ xRadio.setPropertyValue( "DataField", "f_text_enum");
+ }
+
+ /* ------------------------------------------------------------------ */
+ private String getCreateTableStatement( )
+ {
+ String sCreateTableStatement = "CREATE TABLE \"" + s_tableName + "\" (";
+ sCreateTableStatement += "\"ID\" INTEGER NOT NULL PRIMARY KEY,";
+ sCreateTableStatement += "\"f_integer\" INTEGER default NULL,";
+ sCreateTableStatement += "\"f_text\" VARCHAR(50) default NULL,";
+ sCreateTableStatement += "\"f_required_text\" VARCHAR(50) NOT NULL,";
+ sCreateTableStatement += "\"f_decimal\" DECIMAL(10,2) default NULL,";
+ sCreateTableStatement += "\"f_date\" DATE default NULL,";
+ sCreateTableStatement += "\"f_time\" TIME default NULL,";
+ sCreateTableStatement += "\"f_timestamp\" DATETIME default NULL,";
+ sCreateTableStatement += "\"f_blob\" VARBINARY,";
+ sCreateTableStatement += "\"f_text_enum\" VARCHAR(50) default NULL,";
+ sCreateTableStatement += "\"f_tinyint\" TINYINT default NULL";
+ sCreateTableStatement += ");";
+ return sCreateTableStatement;
+ }
+
+ /* ------------------------------------------------------------------ */
+ private String[] getSampleDataValueString( ) throws java.lang.Exception
+ {
+ String[] aValues = new String[] {
+ "1,42,'the answer','foo',0.12,'2003-09-22','15:00:00','2003-09-23 17:15:23',NULL,'none',1",
+ "2,13,'the question','bar',12.43,'2003-09-24','16:18:00','2003-09-24 08:45:12',NULL,'none',0"
+ };
+ return aValues;
+ }
+
+ /* ------------------------------------------------------------------ */
+ private boolean ensureTables() throws com.sun.star.uno.Exception, java.lang.Exception
+ {
+ Connection connection = new Connection( m_dataSource.getConnection( "", "" ) );
+
+ // drop the table, if it already exists
+ if ( !implExecuteStatement( "DROP TABLE \"" + s_tableName + "\" IF EXISTS" )
+ || !implExecuteStatement( getCreateTableStatement() )
+ )
+ {
+ failed( "could not create the required sample table!" );
+ return false;
+ }
+
+ String sInsertionPrefix = "INSERT INTO \"" + s_tableName + "\" VALUES (";
+ String[] aValues = getSampleDataValueString();
+ for ( int i=0; i<aValues.length; ++i )
+ if ( !implExecuteStatement( sInsertionPrefix + aValues[ i ] + ")" ) )
+ {
+ failed( "could not create the required sample data" );
+ return false;
+ }
+
+ connection.refreshTables();
+
+ // do not need the connection anymore
+ connection.close();
+
+ return true;
+ }
+
+ /* ------------------------------------------------------------------ */
+ /// checks the 3 radio buttons for the given states
+ private boolean checkRadios( short stateNone, short stateNormal, short stateImportant ) throws com.sun.star.uno.Exception, java.lang.Exception
+ {
+ if ( ((Short)getRadioModel( "radio_group", "none" ).getPropertyValue( "State" )).shortValue() != stateNone )
+ {
+ failed( "wrong value of the 'none' radio button!" );
+ }
+ else if ( ((Short)getRadioModel( "radio_group", "normal" ).getPropertyValue( "State" )).shortValue() != stateNormal )
+ {
+ failed( "wrong value of the 'normal' radio button!" );
+ }
+ else if ( ((Short)getRadioModel( "radio_group", "important" ).getPropertyValue( "State" )).shortValue() != stateImportant )
+ {
+ failed( "wrong value of the 'important' radio button!" );
+ }
+ else
+ return true;
+
+ return false;
+ }
+
+ /* ------------------------------------------------------------------ */
+ private boolean checkNullValue( String fieldName, String propertyName ) throws com.sun.star.uno.Exception, java.lang.Exception
+ {
+ Object value = getControlModel( fieldName ).getPropertyValue( propertyName );
+ if ( !util.utils.isVoid( value ) )
+ {
+ log.println( "wrong value of the " + fieldName + " field!" );
+ log.println( " expected: <null/>" );
+ log.println( " found : " + value.toString() );
+ }
+ else
+ return true;
+
+ return false;
+ }
+
+ /* ------------------------------------------------------------------ */
+ private boolean checkIntValue( int requiredValue, String fieldName, String propertyName ) throws com.sun.star.uno.Exception, java.lang.Exception
+ {
+ try
+ {
+ if ( "f_time".equals(fieldName) )
+ // http://bugs.mysql.com/bug.php?id=5681
+ return true;
+ if (fieldName == null) {
+ return false;
+ }
+ int currentValue = ((Integer)getControlModel( fieldName ).getPropertyValue( propertyName )).intValue();
+ if ( currentValue != requiredValue )
+ {
+ log.println( "wrong value of the " + fieldName + " field!" );
+ log.println( " expected: " + requiredValue );
+ log.println( " found : " + currentValue );
+ }
+ else
+ return true;
+ }
+ catch( com.sun.star.uno.Exception e )
+ {
+ log.println( "caught an exception while retrieving property value '" + propertyName + "' of control model '" + fieldName + "'" );
+ throw e;
+ }
+
+ return false;
+ }
+
+ /* ------------------------------------------------------------------ */
+ private boolean checkShortValue( short requiredValue, String fieldName, String propertyName ) throws com.sun.star.uno.Exception, java.lang.Exception
+ {
+ try
+ {
+ short currentValue = ((Short)getControlModel( fieldName ).getPropertyValue( propertyName )).shortValue();
+ if ( currentValue != requiredValue )
+ {
+ log.println( "wrong value of the " + fieldName + " field!" );
+ log.println( " expected: " + requiredValue );
+ log.println( " found : " + currentValue );
+ }
+ else
+ return true;
+ }
+ catch( com.sun.star.uno.Exception e )
+ {
+ log.println( "caught an exception while retrieving property value '" + propertyName + "' of control model '" + fieldName + "'" );
+ throw e;
+ }
+
+ return false;
+ }
+
+ /* ------------------------------------------------------------------ */
+ private boolean checkDoubleValue( double requiredValue, String fieldName, String propertyName ) throws com.sun.star.uno.Exception, java.lang.Exception
+ {
+ double currentValue = ((Double)getControlModel( fieldName ).getPropertyValue( propertyName )).doubleValue();
+ if ( currentValue != requiredValue )
+ {
+ log.println( "wrong value of the " + fieldName + " field!" );
+ log.println( " expected: " + requiredValue );
+ log.println( " found : " + currentValue );
+ }
+ else
+ return true;
+
+ return false;
+ }
+
+ /* ------------------------------------------------------------------ */
+ private boolean checkStringValue( String requiredValue, String fieldName, String propertyName ) throws com.sun.star.uno.Exception, java.lang.Exception
+ {
+ String currentValue = (String)getControlModel( fieldName ).getPropertyValue( propertyName );
+ if ( !currentValue.equals( requiredValue ) )
+ {
+ log.println( "wrong value of the " + fieldName + " field!" );
+ log.println( " expected: " + requiredValue );
+ log.println( " found : " + currentValue );
+ }
+ else
+ return true;
+
+ return false;
+ }
+
+ /* ------------------------------------------------------------------ */
+ private XPropertySet getRadioModel( String name, String refValue ) throws com.sun.star.uno.Exception, java.lang.Exception
+ {
+ return m_formLayer.getRadioModelByRefValue( m_masterForm, name, refValue );
+ }
+
+ /* ------------------------------------------------------------------ */
+ /** executes the given statement on the given connection
+ */
+ protected boolean implExecuteStatement( String sStatement ) throws java.lang.Exception
+ {
+ try
+ {
+ m_databaseDocument.executeSQL( sStatement );
+ }
+ catch(com.sun.star.sdbc.SQLException e)
+ {
+ System.err.println( e );
+ return false;
+ }
+
+ return true;
+ }
+
+ /* ------------------------------------------------------------------ */
+ /** simulates a user's text input into a control given by model name
+ */
+ private void userTextInput( String modelName, String text, boolean withCommit ) throws com.sun.star.uno.Exception, java.lang.Exception
+ {
+ XPropertySet controlModel = getControlModel( modelName );
+ // the form runtime environment (namely the form controller) rely on focus events for recognizing
+ // control content changes ...
+ if ( withCommit )
+ m_document.getCurrentView().grabControlFocus( controlModel );
+
+ m_formLayer.userTextInput( controlModel, text );
+
+ // focus back to a dummy control model so the content of the model we just changed will
+ // be committed to the underlying database column
+ if ( withCommit )
+ m_document.getCurrentView().grabControlFocus( getControlModel( "dummy" ) );
+ }
+
+ /* ------------------------------------------------------------------ */
+ private void moveToInsertRow() throws com.sun.star.uno.Exception, java.lang.Exception
+ {
+ XResultSetUpdate xResultSet = UnoRuntime.queryInterface( XResultSetUpdate.class, m_masterForm );
+ xResultSet.moveToInsertRow( );
+ }
+
+ /* ------------------------------------------------------------------ */
+ private void moveToFirst() throws com.sun.star.uno.Exception, java.lang.Exception
+ {
+ XResultSet xResultSet = UnoRuntime.queryInterface( XResultSet.class, m_masterForm );
+ xResultSet.first( );
+ }
+
+ /* ------------------------------------------------------------------ */
+ private void moveToNext() throws com.sun.star.uno.Exception, java.lang.Exception
+ {
+ XResultSet xResultSet = UnoRuntime.queryInterface( XResultSet.class, m_masterForm );
+ xResultSet.next( );
+ }
+
+ /* ------------------------------------------------------------------ */
+ /** simulates pressing a toolbox button with the given URL
+ */
+ private void executeSlot( String slotURL ) throws java.lang.Exception
+ {
+ XDispatch xDispatch = m_document.getCurrentView().getDispatcher( slotURL );
+
+ URL[] url = new URL[] { new URL() };
+ url[0].Complete = slotURL;
+ XURLTransformer xTransformer = UnoRuntime.queryInterface(
+ XURLTransformer.class, m_orb.createInstance( "com.sun.star.util.URLTransformer" ) );
+ xTransformer.parseStrict( url );
+
+ PropertyValue[] aArgs = new PropertyValue[0];
+ xDispatch.dispatch( url[0], aArgs );
+ }
+
+ /* ------------------------------------------------------------------ */
+ /** undos the changes on the current record, by simulating pressing of the respective toolbox button
+ */
+ private void undoRecordByUI() throws java.lang.Exception
+ {
+ executeSlot( ".uno:RecUndo" );
+ }
+
+ /* ------------------------------------------------------------------ */
+ /** saves the current record, by simulating pressing of the respective toolbox button
+ */
+ private void saveRecordByUI() throws java.lang.Exception
+ {
+ executeSlot( ".uno:RecSave" );
+ }
+
+ /* ------------------------------------------------------------------ */
+ /** moves to the next record, by simulating pressing of the respective toolbox button
+ */
+ private void nextRecordByUI() throws java.lang.Exception
+ {
+ executeSlot( ".uno:NextRecord" );
+ }
+ /* ------------------------------------------------------------------ */
+ /** moves to the previous record, by simulating pressing of the respective toolbox button
+ */
+ private void previousRecordByUI() throws java.lang.Exception
+ {
+ executeSlot( ".uno:PrevRecord" );
+ }
+
+ /* ------------------------------------------------------------------ */
+ private void createImageFile() throws java.io.IOException
+ {
+ m_sImageURL = util.utils.getOfficeTempDir( m_orb ) + "image.gif";
+
+ FileOutputStream aFile = null;
+ try
+ {
+ aFile = new FileOutputStream( m_sImageURL );
+ aFile.write( getSamplePicture() );
+ }
+ finally
+ {
+ if ( aFile != null )
+ aFile.close();
+ }
+ log.println( "created temporary image file: " + m_sImageURL );
+
+ // for later setting the url at the image control, we need a real URL, no system path
+ m_sImageURL = util.utils.getOfficeTemp( m_orb ) + "image.gif";
+ }
+
+ /* ------------------------------------------------------------------ */
+ private byte[] getSamplePicture()
+ {
+ byte[] aBytes = new byte[] {
+ (byte)0x47, (byte)0x49, (byte)0x46, (byte)0x38, (byte)0x39, (byte)0x61, (byte)0x0A, (byte)0x00, (byte)0x0A, (byte)0x00, (byte)0xB3, (byte)0x00, (byte)0x00, (byte)0xFF, (byte)0x00, (byte)0x00,
+ (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0x00, (byte)0x00, (byte)0xFF, (byte)0xFF, (byte)0x00, (byte)0xFF, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0xFF, (byte)0xFF,
+ (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF,
+ (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0x2C, (byte)0x00, (byte)0x00,
+ (byte)0x00, (byte)0x00, (byte)0x0A, (byte)0x00, (byte)0x0A, (byte)0x00, (byte)0x00, (byte)0x04, (byte)0x20, (byte)0x10, (byte)0xC8, (byte)0x49, (byte)0x41, (byte)0xB9, (byte)0xF8, (byte)0xCA,
+ (byte)0x12, (byte)0xBA, (byte)0x2F, (byte)0x5B, (byte)0x30, (byte)0x8C, (byte)0x43, (byte)0x00, (byte)0x5A, (byte)0x22, (byte)0x41, (byte)0x94, (byte)0x27, (byte)0x37, (byte)0xA8, (byte)0x6C,
+ (byte)0x48, (byte)0xC6, (byte)0xA8, (byte)0xD7, (byte)0xB5, (byte)0x19, (byte)0x56, (byte)0xED, (byte)0x11, (byte)0x00, (byte)0x3B
+ };
+
+ return aBytes;
+ }
+
+ /* ------------------------------------------------------------------ */
+ private byte[] getSamplePictureBytes()
+ {
+ byte[] aBytes = new byte[] {
+ (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x05, (byte)0x05, (byte)0x05, (byte)0x05, (byte)0x05,
+ (byte)0x05, (byte)0x05, (byte)0x05, (byte)0x00, (byte)0x00, (byte)0x05, (byte)0x01, (byte)0x01, (byte)0x01, (byte)0x01, (byte)0x01, (byte)0x01, (byte)0x05, (byte)0x00, (byte)0x00, (byte)0x05,
+ (byte)0x01, (byte)0x03, (byte)0x03, (byte)0x03, (byte)0x03, (byte)0x01, (byte)0x05, (byte)0x00, (byte)0x00, (byte)0x05, (byte)0x01, (byte)0x03, (byte)0x04, (byte)0x04, (byte)0x03, (byte)0x01,
+ (byte)0x05, (byte)0x00, (byte)0x00, (byte)0x05, (byte)0x01, (byte)0x03, (byte)0x04, (byte)0x04, (byte)0x03, (byte)0x01, (byte)0x05, (byte)0x00, (byte)0x00, (byte)0x05, (byte)0x01, (byte)0x03,
+ (byte)0x03, (byte)0x03, (byte)0x03, (byte)0x01, (byte)0x05, (byte)0x00, (byte)0x00, (byte)0x05, (byte)0x01, (byte)0x01, (byte)0x01, (byte)0x01, (byte)0x01, (byte)0x01, (byte)0x05, (byte)0x00,
+ (byte)0x00, (byte)0x05, (byte)0x05, (byte)0x05, (byte)0x05, (byte)0x05, (byte)0x05, (byte)0x05, (byte)0x05, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00,
+ (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00
+ };
+ return aBytes;
+ }
+
+ /* ------------------------------------------------------------------ */
+ private boolean verifyReferenceImage( byte[] referenceBytes ) throws com.sun.star.uno.Exception, java.lang.Exception
+ {
+ XPropertySet xImageModel = getControlModel( "f_blob" );
+
+ // check if the image control properly says that there currently is no image on the first record
+ XImageProducerSupplier xSuppProducer = UnoRuntime.queryInterface( XImageProducerSupplier.class,
+ xImageModel );
+ XImageProducer xProducer = xSuppProducer.getImageProducer();
+
+ ImageComparison compareImages = new ImageComparison( referenceBytes, this );
+ synchronized( this )
+ {
+ xProducer.addConsumer( compareImages );
+ xProducer.startProduction();
+ }
+ xProducer.removeConsumer( compareImages );
+
+ return compareImages.imagesEqual( );
+ }
+
+ /* ------------------------------------------------------------------ */
+ public void errorOccured( SQLErrorEvent _event )
+ {
+ // just remember for the moment
+ m_mostRecentErrorEvent = _event;
+ }
+
+ /* ------------------------------------------------------------------ */
+ public void disposing( EventObject _event )
+ {
+ // not interested in
+ }
+}
diff --git a/forms/qa/integration/forms/FormLayer.java b/forms/qa/integration/forms/FormLayer.java
new file mode 100644
index 000000000..3d28ff054
--- /dev/null
+++ b/forms/qa/integration/forms/FormLayer.java
@@ -0,0 +1,318 @@
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+package integration.forms;
+
+import com.sun.star.accessibility.XAccessible;
+import com.sun.star.accessibility.XAccessibleEditableText;
+import com.sun.star.uno.UnoRuntime;
+
+import com.sun.star.beans.XPropertySet;
+import com.sun.star.container.XIndexContainer;
+import com.sun.star.container.XIndexAccess;
+import com.sun.star.lang.XMultiServiceFactory;
+import com.sun.star.drawing.XControlShape;
+import com.sun.star.drawing.XShapes;
+import com.sun.star.awt.Size;
+import com.sun.star.awt.Point;
+import com.sun.star.awt.VisualEffect;
+import com.sun.star.awt.XControlModel;
+import com.sun.star.text.TextContentAnchorType;
+import com.sun.star.drawing.XDrawPage;
+
+public class FormLayer
+{
+ private final DocumentHelper m_document;
+ private XDrawPage m_page;
+
+ /* ------------------------------------------------------------------ */
+ /** Creates a new instance of FormLayer */
+ public FormLayer( DocumentHelper _document )
+ {
+ m_document = _document;
+ }
+
+ /* ------------------------------------------------------------------ */
+ /** sets the page which is to be used for subsequent insertions of controls/shapes
+ */
+ void setInsertPage( int page ) throws com.sun.star.lang.IndexOutOfBoundsException, com.sun.star.lang.WrappedTargetException
+ {
+ m_page = m_document.getDrawPage( page );
+ }
+
+ /* ------------------------------------------------------------------ */
+ /** 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 nWidth
+ the width of the newly inserted shape
+ @param nHeight
+ the height of the newly inserted shape
+ @param _parentForm
+ the form to use as parent for the newly create form component. May be null, in this case
+ a default parent is chosen by the implementation
+ @return
+ the property access to the control's model
+ */
+ public XPropertySet createControlAndShape( String sFormComponentService, int nXPos,
+ int nYPos, int nWidth, int nHeight, Object _parentForm ) throws java.lang.Exception
+ {
+ // let the document create a shape
+ XMultiServiceFactory xDocAsFactory = UnoRuntime.queryInterface(
+ XMultiServiceFactory.class, m_document.getDocument() );
+ XControlShape xShape = 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 ) );
+
+ // adjust the anchor so that the control is tied to the page
+ XPropertySet xShapeProps = dbfTools.queryPropertySet( xShape );
+ TextContentAnchorType 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 = UnoRuntime.queryInterface( XControlModel.class,
+ m_document.getOrb().createInstance( sQualifiedComponentName ) );
+
+ // insert the model into the form component hierarchy, if the caller gave us a location
+ if ( null != _parentForm )
+ {
+ XIndexContainer parentForm;
+ if ( _parentForm instanceof XIndexContainer )
+ parentForm = (XIndexContainer)_parentForm;
+ else
+ parentForm = UnoRuntime.queryInterface( XIndexContainer.class, _parentForm );
+ parentForm.insertByIndex( parentForm.getCount(), xModel );
+ }
+
+ // knitt them
+ xShape.setControl( xModel );
+
+ // add the shape to the shapes collection of the document
+ XDrawPage pageWhereToInsert = ( m_page != null ) ? m_page : m_document.getMainDrawPage();
+
+ XShapes xDocShapes = UnoRuntime.queryInterface( XShapes.class, pageWhereToInsert );
+ xDocShapes.add( xShape );
+
+ // and outta here with the XPropertySet interface of the model
+ XPropertySet xModelProps = dbfTools.queryPropertySet( xModel );
+ return xModelProps;
+ }
+
+ /* ------------------------------------------------------------------ */
+ /** 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 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 XPropertySet createControlAndShape( String sFormComponentService, int nXPos,
+ int nYPos, int nWidth, int nHeight ) throws java.lang.Exception
+ {
+ return createControlAndShape( sFormComponentService, nXPos, nYPos, nWidth, nHeight, null );
+ }
+
+ /** creates a pair of controls, namely a label control, and another control labeled by it
+ *
+ * @param _formComponentServiceName
+ * the service name for the control which is not the label control
+ * @param _label
+ * the label to be shown in the label control
+ * @param _xPos
+ * the horizontal position of the control pair
+ * @param _yPos
+ * the vertical position of the control pair
+ * @param _height
+ * the height of the control which is not the label control
+ * @return
+ * the model of the control which is not the label control
+ */
+ public XPropertySet createLabeledControl( String _formComponentServiceName, String _label, int _xPos,
+ int _yPos, int _height )
+ throws java.lang.Exception
+ {
+ // insert the label control
+ XPropertySet label = createControlAndShape( "FixedText", _xPos, _yPos, 25, 6 );
+ label.setPropertyValue( "Label", _label );
+
+ // insert the text field control
+ XPropertySet field = createControlAndShape( _formComponentServiceName,
+ _xPos + 25, _yPos, 40, _height );
+ // knit it to its label component
+ field.setPropertyValue( "LabelControl", label );
+
+ // names
+ label.setPropertyValue( "Name", _label + "_Label" );
+ field.setPropertyValue( "Name", _label );
+
+ return field;
+ }
+
+ /* ------------------------------------------------------------------ */
+ /** creates a line of controls, consisting of a label and a field for data input.
+
+ <p>In opposite to the second form of this method, here the height of the field,
+ as well as the abscissa of the label, are under the control of the caller.</p>
+
+ @param sControlType
+ specifies the type of the data input control
+ @param sFieldName
+ specifies the field name the text field should be bound to
+ @param _controlNamePostfix
+ specifies a postfix to append to the logical control names
+ @param nYPos
+ specifies the Y position of the line to start at
+ @param nHeight
+ the height of the field
+ @return
+ the control model of the created data input field
+ */
+ public XPropertySet insertControlLine( String sControlType, String sFieldName, String _controlNamePostfix,
+ int nXPos, int nYPos, int nHeight )
+ throws java.lang.Exception
+ {
+ // insert the label control
+ XPropertySet xLabelModel = createControlAndShape( "FixedText", nXPos, nYPos, 25, 6 );
+ xLabelModel.setPropertyValue( "Label", sFieldName );
+
+ // insert the text field control
+ XPropertySet xFieldModel = createControlAndShape( sControlType, nXPos + 26, nYPos, 40, nHeight );
+ xFieldModel.setPropertyValue( "DataField", sFieldName );
+ if ( xFieldModel.getPropertySetInfo().hasPropertyByName( "Border" ) )
+ {
+ xFieldModel.setPropertyValue( "Border", Short.valueOf( VisualEffect.FLAT ) );
+ if ( xFieldModel.getPropertySetInfo().hasPropertyByName( "BorderColor" ) )
+ xFieldModel.setPropertyValue( "BorderColor", Integer.valueOf( 0x00C0C0C0 ) );
+ }
+ // knit it to its label component
+ xFieldModel.setPropertyValue( "LabelControl", xLabelModel );
+
+ // some names, so later on we can find them
+ if ( _controlNamePostfix == null )
+ _controlNamePostfix = "";
+ xLabelModel.setPropertyValue( "Name", sFieldName + _controlNamePostfix + "_Label" );
+ xFieldModel.setPropertyValue( "Name", sFieldName + _controlNamePostfix );
+
+ return xFieldModel;
+ }
+
+ /* ------------------------------------------------------------------ */
+ /** creates a line of controls, consisting of a label and a field for data input.
+
+ @param sControlType
+ specifies the type of the data input control
+ @param sFieldName
+ specifies the field name the text field should be bound to
+ @param nYPos
+ specifies the Y position of the line to start at
+ @return
+ the control model of the created data input field
+ */
+ public XPropertySet insertControlLine( String sControlType, String sFieldName, String sControlNamePostfix, int nYPos )
+ throws java.lang.Exception
+ {
+ return insertControlLine( sControlType, sFieldName, sControlNamePostfix, 10, nYPos, 6 );
+ }
+
+ /* ------------------------------------------------------------------ */
+ /** retrieves the radio button model with the given name and the given ref value
+ * @param form
+ * the parent form of the radio button model to find
+ * @param name
+ * the name of the radio button
+ * @param refValue
+ * the reference value of the radio button
+ */
+ public XPropertySet getRadioModelByRefValue( XPropertySet form, String name, String refValue ) throws com.sun.star.uno.Exception, java.lang.Exception
+ {
+ XIndexAccess indexAccess = UnoRuntime.queryInterface( XIndexAccess.class, form );
+
+ for ( int i=0; i<indexAccess.getCount(); ++i )
+ {
+ XPropertySet control = dbfTools.queryPropertySet( indexAccess.getByIndex( i ) );
+
+ if ( ((String)control.getPropertyValue( "Name" )).equals( name ) )
+ if ( ((String)control.getPropertyValue( "RefValue" )).equals( refValue ) )
+ return control;
+ }
+ return null;
+ }
+
+
+
+ /* ------------------------------------------------------------------ */
+ /** retrieves a control model with a given (integer) access path
+ */
+ public XPropertySet getControlModel( int[] _accessPath ) throws com.sun.star.uno.Exception
+ {
+ XIndexAccess indexAcc = UnoRuntime.queryInterface( XIndexAccess.class,
+ m_document.getFormComponentTreeRoot() );
+ XPropertySet controlModel = null;
+ int i=0;
+ while ( ( indexAcc != null ) && ( i < _accessPath.length ) )
+ {
+ controlModel = UnoRuntime.queryInterface( XPropertySet.class,
+ indexAcc.getByIndex( _accessPath[i] ) );
+ indexAcc = UnoRuntime.queryInterface( XIndexAccess.class,
+ controlModel );
+ ++i;
+ }
+ return controlModel;
+ }
+
+
+
+ /* ------------------------------------------------------------------ */
+ /** simulates a user's text input into a control given by control model
+ */
+ public void userTextInput( XPropertySet controlModel, String text ) throws com.sun.star.uno.Exception, java.lang.Exception
+ {
+ // we will *not* simply set the value property at the model. This is not the same as
+ // doing a user input, as the latter will trigger a lot of notifications, which the forms runtime environment
+ // (namely the FormController) relies on to notice that the control changed.
+ // Instead, we use the Accessibility interfaces of the control to simulate text input
+ XAccessible formattedAccessible = UnoRuntime.queryInterface( XAccessible.class,
+ m_document.getCurrentView().getControl( controlModel )
+ );
+ XAccessibleEditableText textAccess = UnoRuntime.queryInterface( XAccessibleEditableText.class,
+ formattedAccessible.getAccessibleContext() );
+ textAccess.setText( text );
+ }
+}
diff --git a/forms/qa/integration/forms/FormPropertyBags.java b/forms/qa/integration/forms/FormPropertyBags.java
new file mode 100644
index 000000000..61aa984c1
--- /dev/null
+++ b/forms/qa/integration/forms/FormPropertyBags.java
@@ -0,0 +1,201 @@
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+package integration.forms;
+
+import com.sun.star.beans.PropertyAttribute;
+import com.sun.star.beans.PropertyChangeEvent;
+import com.sun.star.beans.PropertyExistException;
+import com.sun.star.beans.PropertyValue;
+import com.sun.star.beans.PropertyVetoException;
+import com.sun.star.beans.UnknownPropertyException;
+import com.sun.star.beans.XPropertyChangeListener;
+import com.sun.star.beans.XPropertyContainer;
+import com.sun.star.beans.XPropertySet;
+import com.sun.star.beans.XPropertySetInfo;
+import com.sun.star.frame.XStorable;
+import com.sun.star.lang.EventObject;
+import com.sun.star.uno.UnoRuntime;
+
+import com.sun.star.lang.XMultiServiceFactory;
+
+import com.sun.star.util.XCloseable;
+
+public class FormPropertyBags extends complexlib.ComplexTestCase implements XPropertyChangeListener
+{
+ private DocumentHelper m_document;
+ private FormLayer m_formLayer;
+ private XMultiServiceFactory m_orb;
+
+ private PropertyChangeEvent m_propertyChangeEvent;
+
+ /** Creates a new instance of FormPropertyBags */
+ public FormPropertyBags()
+ {
+ m_propertyChangeEvent = new PropertyChangeEvent();
+ }
+
+ /* ------------------------------------------------------------------ */
+ @Override
+ public String[] getTestMethodNames()
+ {
+ return new String[] {
+ "checkSomething"
+ };
+ }
+
+ /* ------------------------------------------------------------------ */
+ @Override
+ public String getTestObjectName()
+ {
+ return "Form Component Property Bag Test";
+ }
+
+ /* ------------------------------------------------------------------ */
+ public void before() throws com.sun.star.uno.Exception, java.lang.Exception
+ {
+ m_orb = param.getMSF();
+ m_document = DocumentHelper.blankTextDocument( m_orb );
+ m_formLayer = new FormLayer( m_document );
+ }
+
+ /* ------------------------------------------------------------------ */
+ private void impl_closeDoc() throws com.sun.star.uno.Exception
+ {
+ if ( m_document != null )
+ {
+ XCloseable closeDoc = UnoRuntime.queryInterface( XCloseable.class, m_document.getDocument() );
+ closeDoc.close( true );
+ }
+ }
+
+ /* ------------------------------------------------------------------ */
+ public void after() throws com.sun.star.uno.Exception, java.lang.Exception
+ {
+ impl_closeDoc();
+ }
+
+ /* ------------------------------------------------------------------ */
+ public void checkSomething() throws com.sun.star.uno.Exception, java.lang.Exception
+ {
+ XPropertySet textFieldModel = m_formLayer.createControlAndShape( "DatabaseTextField", 10, 10, 25, 6 );
+
+ // check whether adding new properties is successful
+ XPropertyContainer propContainer = UnoRuntime.queryInterface(
+ XPropertyContainer.class, textFieldModel );
+ assure("XPropertyContainer not supported!", propContainer != null );
+
+ propContainer.addProperty( "SomeBoundText", PropertyAttribute.BOUND, "InitialBoundText" );
+ propContainer.addProperty( "SomeTransientText", PropertyAttribute.TRANSIENT, "InitialTransientProperty" );
+ propContainer.addProperty( "SomeReadonlyText", PropertyAttribute.READONLY, "InitialReadonlyText" );
+ propContainer.addProperty( "SomeNumericValue", PropertyAttribute.BOUND, Integer.valueOf( 42 ) );
+
+ XPropertySetInfo propertyInfo = textFieldModel.getPropertySetInfo();
+ assure( "Per service definition, dynamic properties are expected to be forced to be removable",
+ ( propertyInfo.getPropertyByName("SomeBoundText").Attributes & PropertyAttribute.REMOVABLE ) != 0 );
+
+ // a second addition of a property with an existent name should be rejected
+ boolean caughtExpected = false;
+ try { propContainer.addProperty( "SomeBoundText", PropertyAttribute.BOUND, "InitialBoundText" ); }
+ catch( PropertyExistException e ) { caughtExpected = true; }
+ catch( Exception e ) { }
+ assure( "repeated additions of a property with the same name should be rejected",
+ caughtExpected );
+
+ // check whether the properties are bound as expected
+ impl_checkPropertyValueNotification( textFieldModel );
+
+ // check property value persistence
+ impl_checkPropertyPersistence();
+ }
+
+ /* ------------------------------------------------------------------ */
+ private void impl_checkPropertyValueNotification( XPropertySet _controlModel ) throws com.sun.star.uno.Exception
+ {
+ _controlModel.addPropertyChangeListener( "", this );
+
+ _controlModel.setPropertyValue( "SomeBoundText", "ChangedBoundText" );
+ assure( "changes in the bound property are not properly notified",
+ m_propertyChangeEvent.PropertyName.equals( "SomeBoundText" )
+ && m_propertyChangeEvent.OldValue.equals( "InitialBoundText" )
+ && m_propertyChangeEvent.NewValue.equals( "ChangedBoundText" ) );
+
+ m_propertyChangeEvent = null;
+ _controlModel.setPropertyValue( "SomeTransientText", "ChangedTransientText" );
+ assure( "changes in non-bound properties should not be notified",
+ m_propertyChangeEvent == null );
+
+ boolean caughtExpected = false;
+ try { _controlModel.setPropertyValue( "SomeReadonlyText", "ChangedReadonlyText" ); }
+ catch( PropertyVetoException e ) { caughtExpected = true; }
+ catch( Exception e ) { }
+ assure( "trying to write a read-only property did not give the expected result",
+ caughtExpected );
+
+ _controlModel.removePropertyChangeListener( "", this );
+ }
+
+ /* ------------------------------------------------------------------ */
+ private void impl_checkPropertyPersistence() throws com.sun.star.uno.Exception
+ {
+ // store the document
+ XStorable store = UnoRuntime.queryInterface( XStorable.class, m_document.getDocument() );
+ String documentURL = util.utils.getOfficeTemp( m_orb ) + "document.odt";
+ PropertyValue[] storeArguments = new PropertyValue[] { new PropertyValue() };
+ storeArguments[0].Name = "FilterName";
+ storeArguments[0].Value = "writer8";
+ store.storeAsURL( documentURL, storeArguments );
+
+ // close and re-load it
+ impl_closeDoc();
+
+ m_document = DocumentHelper.loadDocument( m_orb, documentURL );
+ m_formLayer = new FormLayer( m_document );
+
+ XPropertySet textFieldModel = m_formLayer.getControlModel( new int[] { 0, 0 } );
+
+ // all persistent properties should have the expected values
+ assure( "persistent properties did not survive reload (1)!", ((String)textFieldModel.getPropertyValue( "SomeBoundText" )).equals( "ChangedBoundText" ) );
+ assure( "persistent properties did not survive reload (2)!", ((String)textFieldModel.getPropertyValue( "SomeReadonlyText" )).equals( "InitialReadonlyText" ) );
+// assure( "persistent properties did not survive reload (3)!", ((Integer)textFieldModel.getPropertyValue( "SomeNumericValue" )).equals( Integer.valueOf( 42 ) ) );
+ // cannot check this until the types really survive - at the moment, integers are converted to doubles...
+
+ // the transient property should not have survived
+ boolean caughtExpected = false;
+ try { textFieldModel.getPropertyValue( "SomeTransientText" ); }
+ catch( UnknownPropertyException e ) { caughtExpected = true; }
+ assure( "transient property did survive reload!", caughtExpected );
+
+ // There would be more things to check.
+ // For instance, it would be desirable of the property attributes would have survived
+ // the reload, and the property defaults (XPropertyState).
+ // However, the file format currently doesn't allow for this, so those information
+ // is lost when saving the document.
+ }
+
+ /* ------------------------------------------------------------------ */
+ public void propertyChange(PropertyChangeEvent _propertyChangeEvent)
+ {
+ m_propertyChangeEvent = _propertyChangeEvent;
+ }
+
+ /* ------------------------------------------------------------------ */
+ public void disposing(EventObject eventObject)
+ {
+ // not interested in
+ }
+}
diff --git a/forms/qa/integration/forms/ImageComparison.java b/forms/qa/integration/forms/ImageComparison.java
new file mode 100644
index 000000000..e28771fc7
--- /dev/null
+++ b/forms/qa/integration/forms/ImageComparison.java
@@ -0,0 +1,79 @@
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+package integration.forms;
+
+public final class ImageComparison implements com.sun.star.awt.XImageConsumer
+{
+
+ private final byte[] m_referenceBytes;
+ private int m_referencePosition;
+ private final Object m_notifyDone;
+
+ public boolean imagesEqual( )
+ {
+ return m_referencePosition == m_referenceBytes.length;
+ }
+
+ /** Creates a new instance of ImageComparison */
+ public ImageComparison( byte[] referenceBytes, Object toNotify )
+ {
+ m_referenceBytes = referenceBytes;
+ m_referencePosition = 0;
+ m_notifyDone = toNotify;
+ }
+
+ public void complete(int param, com.sun.star.awt.XImageProducer xImageProducer)
+ {
+ synchronized( m_notifyDone )
+ {
+ m_notifyDone.notify();
+ }
+ }
+
+ public void init(int param, int param1)
+ {
+ }
+
+ public void setColorModel(short param, int[] values, int param2, int param3, int param4, int param5)
+ {
+ }
+
+ public void setPixelsByBytes(int param, int param1, int param2, int param3, byte[] values, int param5, int param6)
+ {
+ if ( m_referencePosition == -1 )
+ // already failed
+ return;
+
+ int i = 0;
+ while ( ( m_referencePosition < m_referenceBytes.length ) && ( i < values.length ) )
+ {
+ if ( m_referenceBytes[ m_referencePosition ] != values[ i ] )
+ {
+ m_referencePosition = -1;
+ break;
+ }
+ ++i;
+ ++m_referencePosition;
+ }
+ }
+
+ public void setPixelsByLongs(int param, int param1, int param2, int param3, int[] values, int param5, int param6)
+ {
+ }
+
+}
diff --git a/forms/qa/integration/forms/ListBox.java b/forms/qa/integration/forms/ListBox.java
new file mode 100644
index 000000000..fc60b4837
--- /dev/null
+++ b/forms/qa/integration/forms/ListBox.java
@@ -0,0 +1,285 @@
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+package integration.forms;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Map;
+
+import com.sun.star.awt.XListBox;
+import com.sun.star.beans.XPropertySet;
+import com.sun.star.container.XChild;
+import com.sun.star.container.XIndexAccess;
+import com.sun.star.container.XNameAccess;
+import com.sun.star.form.ListSourceType;
+import com.sun.star.form.runtime.FormFeature;
+import com.sun.star.form.runtime.XFormController;
+import com.sun.star.form.runtime.XFormOperations;
+import com.sun.star.sdb.CommandType;
+import com.sun.star.sdbc.XParameters;
+import com.sun.star.sdbc.XPreparedStatement;
+import com.sun.star.uno.Exception;
+import com.sun.star.uno.UnoRuntime;
+
+import connectivity.tools.HsqlColumnDescriptor;
+import connectivity.tools.HsqlDatabase;
+import connectivity.tools.HsqlTableDescriptor;
+import connectivity.tools.sdb.Connection;
+
+public class ListBox extends TestCase
+{
+ HsqlDatabase m_database = null;
+ private static final String m_foreignKeyTableName = "foreign_keys";
+
+ public ListBox()
+ {
+ super( DocumentType.WRITER );
+ }
+
+ /* ------------------------------------------------------------------ */
+ @Override
+ public String[] getTestMethodNames()
+ {
+ return new String[] {
+ "checkForeignKeys"
+ };
+ }
+
+ /* ------------------------------------------------------------------ */
+ public void checkForeignKeys() throws com.sun.star.uno.Exception, java.lang.Exception
+ {
+ try
+ {
+ // create the form document
+ prepareDocument();
+
+ final XIndexAccess formsCollection = UnoRuntime.queryInterface( XIndexAccess.class,
+ m_document.getFormComponentTreeRoot() );
+ final XNameAccess form = UnoRuntime.queryInterface( XNameAccess.class, formsCollection.getByIndex(0) );
+
+ final DocumentViewHelper view = m_document.getCurrentView();
+ final XFormController formController = view.getFormController( form );
+ final XFormOperations formOperations = formController.getFormOperations();
+
+ // move through all records, and check that the display values in the list boxes are as expected
+ final String[][] fieldTypesDefinitions = impl_getFieldTypeDefinitions();
+ final String[] fieldTypes = fieldTypesDefinitions[0];
+
+ final String[] displayValues = impl_getDisplayValues();
+
+ formOperations.execute( FormFeature.MoveToFirst );
+ for ( int row=0; row<2; ++row )
+ {
+ StringBuilder failedFieldTypes = new StringBuilder();
+ for ( int i=0; i<fieldTypes.length; ++i )
+ {
+ final String columnFKName = fieldTypes[i] + "_fk";
+ Object listBoxModel = form.getByName( columnFKName );
+ XListBox listBoxControl = UnoRuntime.queryInterface( XListBox.class,
+ view.getControl( listBoxModel ) );
+ if ( !listBoxControl.getSelectedItem().equals( displayValues[row] ) )
+ {
+ if ( failedFieldTypes.length() > 0 )
+ failedFieldTypes.append( ", " );
+ failedFieldTypes.append( fieldTypes[i] );
+ }
+ }
+ /*assure( "The following field types do not work when used as bound list box fields: " + failedFieldTypes.toString() +
+ " (row " + row + ")", failedFieldTypes.length() == 0 );*/
+
+ formOperations.execute( FormFeature.MoveToNext );
+ }
+
+ }
+ finally
+ {
+ closeDocument();
+ }
+ }
+
+ /* ------------------------------------------------------------------ */
+ @Override
+ public void before() throws Exception, java.lang.Exception
+ {
+ super.before();
+ impl_createDatabase();
+ }
+
+ /* ------------------------------------------------------------------ */
+ @Override
+ protected void prepareDocument() throws com.sun.star.uno.Exception, java.lang.Exception
+ {
+ super.prepareDocument();
+ impl_createForm();
+ }
+
+ /* ------------------------------------------------------------------ */
+ private String[][] impl_getFieldTypeDefinitions()
+ {
+ return new String[][] {
+ new String[] {
+ "bigint", "boolean", "date", "decimal", "double", "float", "numeric", "time", "timestamp", "tinyint", "varchar"
+ },
+ new String[] {
+ null, null, null, "(10,2)", null, null, "(10,2)", null, null, null, "(50)"
+ }
+ };
+ }
+
+ /* ------------------------------------------------------------------ */
+ private String[] impl_getTypedValue( final String _asType )
+ {
+ Map< String, String[] > valueMap = new HashMap< String, String[] >();
+ valueMap.put( "bigint", new String[] { "1111111111", "222222222" } );
+ valueMap.put( "boolean", new String[] { "false", "true" } );
+ valueMap.put( "date", new String[] { "2001-01-01", "2002-02-02" } );
+ valueMap.put( "decimal", new String[] { "1.11", "2.22" } );
+ valueMap.put( "double", new String[] { "1.11", "2.22" } );
+ valueMap.put( "float", new String[] { "1.11", "2.22" } );
+ valueMap.put( "numeric", new String[] { "1.11", "2.22" } );
+ valueMap.put( "time", new String[] { "01:01:01", "02:02:02" } );
+ valueMap.put( "timestamp", new String[] { "2001-01-01 01:01:01", "2002-02-02 02:02:02" } );
+ valueMap.put( "tinyint", new String[] { "1", "2" } );
+ valueMap.put( "varchar", new String[] { "first", "second" } );
+
+ return valueMap.get( _asType );
+ }
+
+ /* ------------------------------------------------------------------ */
+ private String[] impl_getDisplayValues()
+ {
+ return new String[] { "one", "two" };
+ }
+
+ /* ------------------------------------------------------------------ */
+ private void impl_createDatabase() throws java.lang.Exception
+ {
+ try
+ {
+ m_database = new HsqlDatabase( m_orb );
+ Connection connection = m_database.defaultConnection();
+ System.out.println( m_database.getDocumentURL() );
+
+ final String[][] fieldDefinitions = impl_getFieldTypeDefinitions();
+ final String[] keyTypes = fieldDefinitions[0];
+ final String[] keyCreationArgs = fieldDefinitions[1];
+
+ ArrayList< HsqlColumnDescriptor > foreignKeyColumns = new ArrayList< HsqlColumnDescriptor >();
+ foreignKeyColumns.add( new HsqlColumnDescriptor( "ID", "integer", HsqlColumnDescriptor.PRIMARY ) );
+
+ ArrayList< String[] > foreignKeyValues = new ArrayList< String[] >();
+
+ StringBuilder foreignKeyInsertSQL = new StringBuilder();
+ foreignKeyInsertSQL.append( "INSERT INTO \"" + m_foreignKeyTableName + "\" VALUES (?" );
+
+ final String[] displayValues = impl_getDisplayValues();
+
+ for ( int i=0; i<keyTypes.length; ++i )
+ {
+ final String tableName = keyTypes[i] + "_pk";
+ final String columnPKName = keyTypes[i] + "_pk";
+ final String columnFKName = keyTypes[i] + "_fk";
+ final String columnType = keyTypes[i] + ( keyCreationArgs[i] != null ? keyCreationArgs[i] : "" );
+ m_database.createTable( new HsqlTableDescriptor( tableName,
+ new HsqlColumnDescriptor[] {
+ new HsqlColumnDescriptor( columnPKName, columnType, HsqlColumnDescriptor.PRIMARY ),
+ new HsqlColumnDescriptor( "content", "varchar(50)" )
+ }
+ ) );
+
+ // insert a few rows
+ StringBuilder sql = new StringBuilder();
+ sql.append( "INSERT INTO \"" );
+ sql.append( tableName );
+ sql.append( "\" VALUES (?, ?)");
+ XPreparedStatement statement = connection.prepareStatement( sql.toString() );
+ XParameters statementParameters = UnoRuntime.queryInterface( XParameters.class, statement );
+
+ final String[] keyValues = impl_getTypedValue( keyTypes[i] );
+
+ for ( int row=0; row<displayValues.length; ++row )
+ {
+ statementParameters.setString( 1, keyValues[row] );
+ statementParameters.setString( 2, displayValues[row] );
+ statement.execute();
+ }
+
+ // remember a column descriptor for later creation of the table with the foreign keys
+ foreignKeyColumns.add( new HsqlColumnDescriptor( columnFKName, columnType, HsqlColumnDescriptor.REQUIRED,
+ tableName, columnPKName ) );
+
+ // remember the data to fill into this table
+ foreignKeyValues.add( keyValues );
+ foreignKeyInsertSQL.append( ", ?" );
+ }
+
+ // create the table taking all those foreign keys
+ m_database.createTable( new HsqlTableDescriptor( m_foreignKeyTableName, foreignKeyColumns.toArray( new HsqlColumnDescriptor[foreignKeyColumns.size()] ) ) );
+ // fill in some data
+ foreignKeyInsertSQL.append( ")" );
+ XPreparedStatement statement = connection.prepareStatement( foreignKeyInsertSQL.toString() );
+ XParameters statementParameters = UnoRuntime.queryInterface( XParameters.class, statement );
+ for ( int row=0; row<2; ++row )
+ {
+ statementParameters.setInt( 1, row );
+ for ( int i=0; i<keyTypes.length; ++i )
+ {
+ statementParameters.setString( i+2, foreignKeyValues.get(i)[row] );
+ }
+ statement.execute();
+ }
+
+ m_database.defaultConnection().refreshTables();
+ }
+ finally
+ {
+ if ( m_database != null )
+ m_database.store();
+ }
+ }
+
+ /* ------------------------------------------------------------------ */
+ private void impl_createForm() throws java.lang.Exception
+ {
+ // a single control for the ID field
+ XPropertySet controlModel = m_formLayer.insertControlLine( "DatabaseNumericField", "ID", null, 10, 10, 6 );
+ // bind the form to the foreign_keys table
+ XPropertySet form = dbfTools.queryPropertySet( dbfTools.getParent( controlModel, XChild.class ) );
+ form.setPropertyValue( "Command", m_foreignKeyTableName );
+ form.setPropertyValue( "CommandType", CommandType.TABLE );
+ form.setPropertyValue( "DataSourceName", m_database.getDocumentURL() );
+
+ // create list boxes for the different foreign keys
+ final String[][] fieldDefinitions = impl_getFieldTypeDefinitions();
+ final String[] fieldTypes = fieldDefinitions[0];
+ for ( int i=0; i<fieldTypes.length; ++i )
+ {
+ final String tableName = fieldTypes[i] + "_pk";
+ final String columnFKName = fieldTypes[i] + "_fk";
+ final String columnPKName = fieldTypes[i] + "_pk";
+ XPropertySet listBoxModel = m_formLayer.insertControlLine( "DatabaseListBox", columnFKName, null, 10, 20 + 10*i, 6 );
+ listBoxModel.setPropertyValue( "Dropdown", Boolean.TRUE );
+ listBoxModel.setPropertyValue( "ListSourceType", ListSourceType.SQL );
+ listBoxModel.setPropertyValue( "ListSource", new String[] { "SELECT \"content\", \"" + columnPKName +
+ "\" FROM \"" + tableName + "\"" } );
+ listBoxModel.setPropertyValue( "BoundColumn", Short.valueOf( (short)1 ) );
+ }
+
+ m_document.getCurrentView().toggleFormDesignMode();
+ }
+ }
diff --git a/forms/qa/integration/forms/ListSelection.java b/forms/qa/integration/forms/ListSelection.java
new file mode 100644
index 000000000..5e9fd8a55
--- /dev/null
+++ b/forms/qa/integration/forms/ListSelection.java
@@ -0,0 +1,280 @@
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+package integration.forms;
+
+import com.sun.star.uno.UnoRuntime;
+import com.sun.star.beans.XPropertySet;
+import com.sun.star.sheet.XSpreadsheet;
+import com.sun.star.sheet.XSpreadsheets;
+import com.sun.star.sheet.XSpreadsheetView;
+import com.sun.star.container.XNamed;
+import com.sun.star.container.XNameContainer;
+import com.sun.star.container.XIndexContainer;
+import com.sun.star.drawing.XDrawPageSupplier;
+import com.sun.star.awt.XControlModel;
+import com.sun.star.awt.XListBox;
+import com.sun.star.script.XLibraryContainer;
+import com.sun.star.script.XEventAttacherManager;
+import com.sun.star.script.ScriptEventDescriptor;
+import com.sun.star.accessibility.XAccessible;
+import com.sun.star.accessibility.XAccessibleContext;
+import com.sun.star.accessibility.XAccessibleSelection;
+import com.sun.star.frame.XStorable;
+
+public class ListSelection extends integration.forms.TestCase
+{
+ /** Creates a new instance of ListSelection */
+ public ListSelection()
+ {
+ super( DocumentType.CALC );
+ }
+
+ /* ------------------------------------------------------------------ */
+ @Override
+ public String[] getTestMethodNames()
+ {
+ return new String[] {
+ "checkUserListSelection"
+ };
+ }
+
+ /* ------------------------------------------------------------------ */
+ @Override
+ public String getTestObjectName()
+ {
+ return "Form Control List Selection Test";
+ }
+
+ /* ------------------------------------------------------------------ */
+ public void checkUserListSelection() throws com.sun.star.uno.Exception, java.lang.Exception
+ {
+ int runs = 5;
+ for ( int i = 0; i < runs; ++i )
+ {
+ log.println( "Round " + ( i + 1 ) + " of " + runs );
+ prepareDocument();
+ impl_clickListBox();
+ synchronized( this ) { this.wait( 1000 ); }
+ closeDocument();
+ }
+ }
+
+ /* ------------------------------------------------------------------ */
+ final private void impl_clickListBox()
+ {
+ try
+ {
+ final int runs = 10;
+ java.util.Random generator = new java.util.Random();
+ for ( int i = 0; i < runs; ++i )
+ {
+ // obtain the active sheet
+ XSpreadsheetView view = m_document.getCurrentView().query( XSpreadsheetView.class );
+ XSpreadsheet activeSheet = view.getActiveSheet();
+
+ // Accessibility access to the list box control in this sheet
+ XAccessible accessibleListBox = UnoRuntime.queryInterface(
+ XAccessible.class, getListBoxControl( activeSheet ) );
+ XAccessibleContext context = accessibleListBox.getAccessibleContext();
+
+ // the first "accessible child" of a list box is its list
+ XAccessibleSelection accessibleList = UnoRuntime.queryInterface(
+ XAccessibleSelection.class, context.getAccessibleChild( 1 ) );
+
+ int selectPosition = generator.nextInt( 5 );
+ String selectSheetName = getListBoxControl( activeSheet ).getItem( (short)selectPosition );
+ accessibleList.selectAccessibleChild( selectPosition );
+ try
+ {
+ synchronized( this )
+ {
+ this.wait( 500 );
+ }
+ }
+ catch( InterruptedException e ) { }
+
+ XNamed sheetName = UnoRuntime.queryInterface( XNamed.class, view.getActiveSheet() );
+ assure( "sheet was not selected as expected!", sheetName.getName().equals( selectSheetName ) );
+ }
+ }
+ catch( com.sun.star.uno.Exception e )
+ {
+ e.printStackTrace( System.err );
+ failed( "caught an exception: " + e.toString() );
+ }
+ }
+
+ /* ------------------------------------------------------------------ */
+ final private void impl_setupListenerScript()
+ {
+ try
+ {
+ XPropertySet docProps = dbfTools.queryPropertySet( m_document.getDocument() );
+ XLibraryContainer basicLibs = UnoRuntime.queryInterface(
+ XLibraryContainer.class, docProps.getPropertyValue( "BasicLibraries" ) );
+ XNameContainer basicLib = basicLibs.createLibrary( "default" );
+
+ String sListSelectionScript =
+ "Option Explicit\n" +
+ "\n" +
+ "Sub onListBoxSelected( oEvent as Object )\n" +
+ " Dim oView as Object\n" +
+ " Dim oSheet as Object\n" +
+ " Dim oSheets as Object\n" +
+ "\n" +
+ " Dim oControlModel as Object\n" +
+ " Dim sSheet as String\n" +
+ "\n" +
+ " if ( oEvent.Selected <> 65535 ) Then\n" +
+ " oControlModel = oEvent.Source.Model\n" +
+ " sSheet = oControlModel.StringItemList( oEvent.Selected )\n" +
+ "\n" +
+ " oSheets = thisComponent.Sheets\n" +
+ " oSheet = oSheets.getByName(sSheet)\n" +
+ "\n" +
+ " oView = thisComponent.CurrentController\n" +
+ " oView.setActiveSheet( oSheet )\n" +
+ " End If\n" +
+ "End Sub\n" +
+ "\n" +
+ "Sub onButtonClicked\n" +
+ " MsgBox \"clicked\"\n" +
+ "End Sub\n";
+
+ basicLib.insertByName( "callbacks", sListSelectionScript );
+ }
+ catch( com.sun.star.uno.Exception e )
+ {
+ e.printStackTrace( System.err );
+ failed( "caught an exception: " + e.toString() );
+ }
+ }
+
+ /* ------------------------------------------------------------------ */
+ final private void impl_assignStarBasicScript( XPropertySet controlModel, String interfaceName, String interfaceMethod, String scriptCode )
+ {
+ try
+ {
+ XIndexContainer parentForm = (XIndexContainer)dbfTools.getParent( controlModel, XIndexContainer.class );
+
+ XEventAttacherManager manager = UnoRuntime.queryInterface(
+ XEventAttacherManager.class, parentForm );
+
+ int containerPosition = -1;
+ for ( int i = 0; i < parentForm.getCount(); ++i )
+ {
+ XPropertySet child = dbfTools.queryPropertySet( parentForm.getByIndex( i ) );
+ if ( child.equals( controlModel ) )
+ {
+ containerPosition = i;
+ break;
+ }
+ }
+ manager.registerScriptEvent( containerPosition, new ScriptEventDescriptor(
+ interfaceName,
+ interfaceMethod,
+ "",
+ "StarBasic",
+ scriptCode
+ ) );
+ }
+ catch( com.sun.star.uno.Exception e )
+ {
+ e.printStackTrace( System.err );
+ failed( "caught an exception: " + e.toString() );
+ }
+ }
+
+ /* ------------------------------------------------------------------ */
+ @Override
+ protected void prepareDocument() throws com.sun.star.uno.Exception, java.lang.Exception
+ {
+ super.prepareDocument();
+ impl_setupListenerScript();
+
+ SpreadsheetDocument document = (SpreadsheetDocument)m_document;
+ XSpreadsheets sheets = document.getSheets();
+
+ // delete all sheets except one
+ String[] sheetNames = sheets.getElementNames();
+ for ( short i = 1; i < sheetNames.length; ++i )
+ sheets.removeByName( sheetNames[ i ] );
+
+ // need 5 sheets
+ String[] newSheetNames = new String[] { "first", "second", "third", "forth", "fifth" };
+
+ // give the first one the right name
+ XNamed sheet = UnoRuntime.queryInterface( XNamed.class,
+ sheets.getByName( sheetNames[ 0 ] )
+ );
+ sheet.setName( newSheetNames[ 0 ] );
+
+ // add some dummy buttons
+ for ( int i = 0; i < 4; ++i )
+ {
+ XPropertySet buttonModel = m_formLayer.createControlAndShape( "CommandButton", 10, 10 + i * 10, 30, 8 );
+ impl_assignStarBasicScript( buttonModel, "XActionListener", "actionPerformed", "document:default.callbacks.onButtonClicked" );
+ }
+
+ // and a list box
+ XPropertySet listBox = m_formLayer.createControlAndShape( "ListBox", 50, 10, 40, 6 );
+ listBox.setPropertyValue( "Dropdown", Boolean.TRUE );
+ listBox.setPropertyValue( "StringItemList", newSheetNames );
+ listBox.setPropertyValue( "Name", "ListBox" );
+
+ impl_assignStarBasicScript( listBox, "XItemListener", "itemStateChanged", "document:default.callbacks.onListBoxSelected" );
+
+ // clone this sheet
+ for ( short i = 1; i < newSheetNames.length; ++i )
+ sheets.copyByName( newSheetNames[0], newSheetNames[i], i );
+
+ // switch the thing to alive mode
+ m_document.getCurrentView().toggleFormDesignMode();
+
+ try
+ {
+ XStorable storable = m_document.query( XStorable.class );
+ java.io.File testFile = java.io.File.createTempFile( getTestObjectName(),".ods");
+ storable.storeAsURL( testFile.getAbsoluteFile().toURI().toURL().toString(), new com.sun.star.beans.PropertyValue[]{} );
+ testFile.deleteOnExit();
+ }
+ catch( java.lang.Throwable e )
+ {
+ e.printStackTrace();
+ failed( "caught an exception: " + e.toString() );
+ }
+ }
+
+ /* ------------------------------------------------------------------ */
+ protected XControlModel getListBoxModel( XSpreadsheet sheet )
+ {
+ XDrawPageSupplier suppPage = UnoRuntime.queryInterface(
+ XDrawPageSupplier.class, sheet );
+ FormComponent formsRoot = new FormComponent( suppPage.getDrawPage() );
+ XControlModel listBoxModel = formsRoot.getByIndex( 0 ).
+ getByName( "ListBox" ).query( XControlModel.class );
+ return listBoxModel;
+ }
+
+ /* ------------------------------------------------------------------ */
+ protected XListBox getListBoxControl( XSpreadsheet sheet ) throws com.sun.star.uno.Exception
+ {
+ return UnoRuntime.queryInterface(
+ XListBox.class, m_document.getCurrentView().getControl( getListBoxModel( sheet ) ) );
+ }
+ }
diff --git a/forms/qa/integration/forms/ListSelection.props b/forms/qa/integration/forms/ListSelection.props
new file mode 100644
index 000000000..92b56bef9
--- /dev/null
+++ b/forms/qa/integration/forms/ListSelection.props
@@ -0,0 +1 @@
+ThreadTimeOut=600000
diff --git a/forms/qa/integration/forms/ListSelectionValidator.java b/forms/qa/integration/forms/ListSelectionValidator.java
new file mode 100644
index 000000000..164110d30
--- /dev/null
+++ b/forms/qa/integration/forms/ListSelectionValidator.java
@@ -0,0 +1,51 @@
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+package integration.forms;
+
+public class ListSelectionValidator extends integration.forms.ControlValidator
+{
+ public String explainInvalid( Object Value )
+ {
+ try
+ {
+ short[] selectionIndexes = (short[])Value;
+ if ( selectionIndexes.length > 2 )
+ return "please 2 entries, at most";
+ }
+ catch( java.lang.Exception e )
+ {
+ return "oops. What's this?";
+ }
+ return "";
+ }
+
+ public boolean isValid( Object Value )
+ {
+ try
+ {
+ short[] selectionIndexes = (short[])Value;
+ return selectionIndexes.length <= 2;
+ }
+ catch( java.lang.Exception e )
+ {
+ }
+ return false;
+ }
+
+}
diff --git a/forms/qa/integration/forms/MasterDetailForms.java b/forms/qa/integration/forms/MasterDetailForms.java
new file mode 100644
index 000000000..4b448fe04
--- /dev/null
+++ b/forms/qa/integration/forms/MasterDetailForms.java
@@ -0,0 +1,400 @@
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+package integration.forms;
+
+import com.sun.star.beans.NamedValue;
+import com.sun.star.beans.XPropertySet;
+import com.sun.star.container.XIndexContainer;
+
+import com.sun.star.uno.UnoRuntime;
+import com.sun.star.lang.XMultiServiceFactory;
+import com.sun.star.container.XNameContainer;
+import com.sun.star.embed.XComponentSupplier;
+import com.sun.star.form.XGridColumnFactory;
+import com.sun.star.form.XGridFieldDataSupplier;
+import com.sun.star.form.XLoadable;
+import com.sun.star.lang.XComponent;
+import com.sun.star.sdb.CommandType;
+import com.sun.star.sdb.XFormDocumentsSupplier;
+import com.sun.star.sdbc.SQLException;
+import com.sun.star.sdbc.XColumnLocate;
+import com.sun.star.ucb.Command;
+import com.sun.star.ucb.OpenMode;
+import com.sun.star.ucb.XCommandProcessor;
+import com.sun.star.uno.Type;
+import com.sun.star.util.XModifiable;
+import connectivity.tools.CRMDatabase;
+import connectivity.tools.HsqlColumnDescriptor;
+import connectivity.tools.HsqlDatabase;
+import connectivity.tools.HsqlTableDescriptor;
+import org.openoffice.complex.forms.tools.ResultSet;
+
+
+public class MasterDetailForms extends complexlib.ComplexTestCase implements com.sun.star.form.XLoadListener
+{
+ private XMultiServiceFactory m_orb;
+
+ private XPropertySet m_masterForm;
+ private XPropertySet m_detailForm;
+ private ResultSet m_masterResult;
+ private ResultSet m_detailResult;
+
+ final private Object m_waitForLoad = new Object();
+ private boolean m_loaded = false;
+
+ /* ------------------------------------------------------------------ */
+ @Override
+ public String[] getTestMethodNames()
+ {
+ return new String[] {
+ "checkMultipleKeys",
+ "checkDetailFormDefaults"
+ };
+ }
+
+ /* ------------------------------------------------------------------ */
+ public void before()
+ {
+ m_orb = param.getMSF();
+ }
+
+ /* ------------------------------------------------------------------ */
+ @Override
+ public String getTestObjectName()
+ {
+ return "Form Control Spreadsheet Cell Binding Test";
+ }
+
+ /* ------------------------------------------------------------------ */
+ /** creates the table structure needed for the test
+ */
+ private void impl_createTableStructure( final HsqlDatabase _databaseDocument ) throws SQLException
+ {
+ HsqlColumnDescriptor[] masterColumns = {
+ new HsqlColumnDescriptor( "ID1", "INTEGER", HsqlColumnDescriptor.PRIMARY ),
+ new HsqlColumnDescriptor( "ID2", "INTEGER", HsqlColumnDescriptor.PRIMARY ),
+ new HsqlColumnDescriptor( "value", "VARCHAR(50)" ),
+ };
+ HsqlColumnDescriptor[] detailColumns = {
+ new HsqlColumnDescriptor( "ID", "INTEGER", HsqlColumnDescriptor.PRIMARY ),
+ new HsqlColumnDescriptor( "FK_ID1", "INTEGER", HsqlColumnDescriptor.REQUIRED, "master", "ID1" ),
+ new HsqlColumnDescriptor( "FK_ID2", "INTEGER", HsqlColumnDescriptor.REQUIRED, "master", "ID2" ),
+ new HsqlColumnDescriptor( "name", "VARCHAR(50)" ),
+ };
+ _databaseDocument.createTable( new HsqlTableDescriptor( "master", masterColumns ) );
+ _databaseDocument.createTable( new HsqlTableDescriptor( "detail", detailColumns ) );
+
+ _databaseDocument.executeSQL( "INSERT INTO \"master\" VALUES ( 1, 1, 'First Record' )" );
+ _databaseDocument.executeSQL( "INSERT INTO \"master\" VALUES ( 1, 2, 'Second Record' )" );
+ _databaseDocument.executeSQL( "INSERT INTO \"detail\" VALUES ( 1, 1, 1, 'record 1.1 (1)')");
+ _databaseDocument.executeSQL( "INSERT INTO \"detail\" VALUES ( 2, 1, 1, 'record 1.1 (2)')");
+ _databaseDocument.executeSQL( "INSERT INTO \"detail\" VALUES ( 3, 1, 2, 'record 1.2 (1)')");
+
+ _databaseDocument.defaultConnection().refreshTables();
+ }
+
+ /* ------------------------------------------------------------------ */
+ private void impl_createForms( final HsqlDatabase _databaseDocument ) throws com.sun.star.uno.Exception
+ {
+ m_masterForm = dbfTools.queryPropertySet( m_orb.createInstance( "com.sun.star.form.component.DataForm" ) );
+ m_masterForm.setPropertyValue( "ActiveConnection", _databaseDocument.defaultConnection().getXConnection() );
+ m_masterForm.setPropertyValue( "CommandType", Integer.valueOf( com.sun.star.sdb.CommandType.TABLE ) );
+ m_masterForm.setPropertyValue( "Command", "master" );
+
+ m_masterResult = new ResultSet( m_masterForm );
+
+ m_detailForm = dbfTools.queryPropertySet( m_orb.createInstance( "com.sun.star.form.component.DataForm" ) );
+ m_detailForm.setPropertyValue( "ActiveConnection", _databaseDocument.defaultConnection().getXConnection() );
+ m_detailForm.setPropertyValue( "CommandType", Integer.valueOf( com.sun.star.sdb.CommandType.TABLE ) );
+ m_detailForm.setPropertyValue( "Command", "detail" );
+
+ m_detailResult = new ResultSet( m_detailForm );
+
+ XNameContainer masterContainer = UnoRuntime.queryInterface( XNameContainer.class, m_masterForm );
+ masterContainer.insertByName( "slave", m_detailForm );
+ }
+
+ /* ------------------------------------------------------------------ */
+ /** checks if master-detail relationships including multiple keys work
+ */
+ public void checkMultipleKeys() throws com.sun.star.uno.Exception, java.lang.Exception
+ {
+ HsqlDatabase databaseDocument = null;
+ try
+ {
+ databaseDocument = new HsqlDatabase( m_orb );
+ impl_createTableStructure( databaseDocument );
+ impl_createForms( databaseDocument );
+
+ m_detailForm.setPropertyValue( "MasterFields", new String[] { "ID1", "ID2" } );
+ m_detailForm.setPropertyValue( "DetailFields", new String[] { "FK_ID1", "FK_ID2" } );
+
+ XLoadable loadMaster = UnoRuntime.queryInterface( XLoadable.class, m_masterForm );
+ XLoadable loadDetail = UnoRuntime.queryInterface( XLoadable.class, m_detailForm );
+ loadDetail.addLoadListener( this );
+
+ // wait until the detail form is loaded
+ loadMaster.load();
+ impl_waitForLoadedEvent();
+
+ // okay, now the master form should be on the first record
+ assure( "wrong form state after loading (ID1)", m_masterResult.getInt(1) == 1 );
+ assure( "wrong form state after loading (ID2)", m_masterResult.getInt(2) == 1 );
+ assure( "wrong form state after loading (value)", m_masterResult.getString(3).equals( "First Record" ) );
+
+ // the values in the linked fields should be identical
+ int expectedDetailRowCounts[] = { 2, 1 };
+ do
+ {
+ verifyColumnValueIdentity( "ID1", "FK_ID1" );
+ verifyColumnValueIdentity( "ID2", "FK_ID2" );
+
+ m_detailResult.last();
+ int masterPos = m_masterResult.getRow();
+ assure( "wrong number of records in detail form, for master form at pos " + masterPos,
+ ((Integer)m_detailForm.getPropertyValue( "RowCount" )).intValue() == expectedDetailRowCounts[ masterPos - 1 ] );
+
+ if (!m_masterResult.next())
+ return;
+ impl_waitForLoadedEvent();
+ }
+ while ( !m_masterResult.isAfterLast() );
+ assure( "wrong number of records in master form", 2 == ((Integer)m_masterForm.getPropertyValue( "RowCount" )).intValue() );
+ }
+ finally
+ {
+ if ( databaseDocument != null )
+ databaseDocument.closeAndDelete();
+ impl_cleanUpStep();
+ }
+ }
+
+ /* ------------------------------------------------------------------ */
+ private final void impl_cleanUpStep()
+ {
+ if ( m_masterForm != null )
+ dbfTools.disposeComponent( m_masterForm );
+ if ( m_detailForm != null )
+ dbfTools.disposeComponent( m_detailForm );
+ m_masterForm = m_detailForm = null;
+ }
+
+ /* ------------------------------------------------------------------ */
+ /** checks whether default values in detail forms work as expected.
+ *
+ * Effectively, this test case verifies the issues #i106574# and #i105235# did not creep back in.
+ */
+ public void checkDetailFormDefaults() throws Exception
+ {
+ CRMDatabase database = null;
+ XCommandProcessor subComponentCommands = null;
+ try
+ {
+ // create our standard CRM database document
+ database = new CRMDatabase( m_orb, true );
+
+ // create a form document therein
+ XFormDocumentsSupplier formDocSupp = UnoRuntime.queryInterface( XFormDocumentsSupplier.class, database.getDatabase().getModel() );
+ XMultiServiceFactory formFactory = UnoRuntime.queryInterface( XMultiServiceFactory.class, formDocSupp.getFormDocuments() );
+ NamedValue[] loadArgs = new NamedValue[] {
+ new NamedValue( "ActiveConnection", database.getConnection().getXConnection() ),
+ new NamedValue( "MediaType", "application/vnd.oasis.opendocument.text" )
+ };
+
+ subComponentCommands = UnoRuntime.queryInterface(
+ XCommandProcessor.class,
+ formFactory.createInstanceWithArguments( "com.sun.star.sdb.DocumentDefinition", loadArgs ) );
+ Command command = new Command();
+ command.Name = "openDesign";
+ command.Argument = Short.valueOf( OpenMode.DOCUMENT );
+
+ DocumentHelper subDocument = new DocumentHelper( m_orb,
+ UnoRuntime.queryInterface( XComponent.class,
+ subComponentCommands.execute( command, subComponentCommands.createCommandIdentifier(), null )
+ )
+ );
+ FormLayer formLayer = new FormLayer( subDocument );
+ XPropertySet controlModel = formLayer.insertControlLine( "DatabaseNumericField", "ID", "", 10 );
+ formLayer.insertControlLine( "DatabaseTextField", "Name", "", 20 );
+ formLayer.insertControlLine( "DatabaseTextField", "Description", "", 30 );
+
+ m_masterForm = (XPropertySet)dbfTools.getParent( controlModel, XPropertySet.class );
+ m_masterForm.setPropertyValue( "Command", "categories" );
+ m_masterForm.setPropertyValue( "CommandType", Integer.valueOf( CommandType.TABLE ) );
+
+ // create a detail form
+ m_detailForm = UnoRuntime.queryInterface( XPropertySet.class, subDocument.createSubForm( m_masterForm, "products" ) );
+ m_detailForm.setPropertyValue( "Command", "SELECT \"ID\", \"Name\", \"CategoryID\" FROM \"products\"" );
+ m_detailForm.setPropertyValue( "CommandType", Integer.valueOf( CommandType.COMMAND ) );
+ m_detailForm.setPropertyValue( "MasterFields", new String[] { "ID" } );
+ m_detailForm.setPropertyValue( "DetailFields", new String[] { "CategoryID" } );
+
+ // create a grid control in the detail form, with some columns
+ XPropertySet gridControlModel = formLayer.createControlAndShape( "GridControl", 20, 40, 130, 50, m_detailForm );
+ gridControlModel.setPropertyValue( "Name", "product list" );
+ XIndexContainer gridColumns = UnoRuntime.queryInterface( XIndexContainer.class, gridControlModel );
+ impl_createGridColumn( gridColumns, "TextField", "ID" );
+ XPropertySet nameColumn = impl_createGridColumn( gridColumns, "TextField", "Name" );
+ nameColumn.setPropertyValue( "Width", Integer.valueOf( 600 ) ); // 6 cm
+ nameColumn.setPropertyValue( "DefaultText", "default text" );
+
+ // go live
+ m_masterResult = new ResultSet( m_masterForm );
+ m_detailResult = new ResultSet( m_detailForm );
+
+ XLoadable loadDetail = UnoRuntime.queryInterface( XLoadable.class, m_detailForm );
+ loadDetail.addLoadListener( this );
+
+ subDocument.getCurrentView().toggleFormDesignMode();
+ impl_waitForLoadedEvent();
+
+ // now that we set up this, do the actual tests
+ // First, https://bz.apache.org/ooo/show_bug.cgi?id=105235 described the problem
+ // that default values in the sub form didn't work when the master form was navigated to a row
+ // for which no detail records were present, and the default of the column/control is the same
+ // as the last known value.
+
+ // so, take the current value of the "Name" column, and set it as default value ...
+ String defaultValue = m_detailResult.getString( 2 );
+ nameColumn.setPropertyValue( "DefaultText", defaultValue );
+ // ... then move to the second main form row ...
+ m_masterResult.absolute( 2 );
+ impl_waitForLoadedEvent();
+ // ... which should result in an empty sub form ...
+ assure( "test precondition not met: The second master form record is expected to have no detail records, " +
+ "else the test becomes meaningless", impl_isNewRecord( m_detailForm ) );
+ // ... and in the "Name" column having the proper text
+ String actualValue = (String)nameColumn.getPropertyValue( "Text" );
+ assureEquals( "#i105235#: default value in sub form not working (not propagated to column model)", defaultValue, actualValue );
+ // However, checking the column model's value alone is not enough - we need to ensure it is properly
+ // propagated to the control.
+ XGridFieldDataSupplier gridData = subDocument.getCurrentView().getControl(
+ gridControlModel, XGridFieldDataSupplier.class );
+ actualValue = (String)(gridData.queryFieldData( 0, Type.STRING )[1]);
+ assureEquals( "#i105235#: default value in sub form not working (not propagated to column)", defaultValue, actualValue );
+ }
+ finally
+ {
+ if ( subComponentCommands != null )
+ {
+ XComponentSupplier componentSupplier = UnoRuntime.queryInterface( XComponentSupplier.class, subComponentCommands );
+ XModifiable modifySubComponent = UnoRuntime.queryInterface( XModifiable.class, componentSupplier.getComponent() );
+ modifySubComponent.setModified( false );
+ Command command = new Command();
+ command.Name = "close";
+ subComponentCommands.execute( command, subComponentCommands.createCommandIdentifier(), null );
+ }
+
+ if ( database != null )
+ database.saveAndClose();
+
+ impl_cleanUpStep();
+ }
+ }
+
+ /* ------------------------------------------------------------------ */
+ private boolean impl_isNewRecord( final XPropertySet _rowSet )
+ {
+ boolean isNew = false;
+ try
+ {
+ isNew = ((Boolean)_rowSet.getPropertyValue( "IsNew" )).booleanValue();
+ }
+ catch ( Exception ex )
+ {
+ failed( "obtaining the IsNew property failed" );
+ }
+ return isNew;
+ }
+
+ /* ------------------------------------------------------------------ */
+ private XPropertySet impl_createGridColumn( final XIndexContainer _gridModel, final String _columnType, final String _boundField ) throws Exception
+ {
+ final XGridColumnFactory columnFactory = UnoRuntime.queryInterface( XGridColumnFactory.class, _gridModel );
+ XPropertySet column = columnFactory.createColumn( _columnType );
+ column.setPropertyValue( "DataField", _boundField );
+ column.setPropertyValue( "Name", _boundField );
+ column.setPropertyValue( "Label", _boundField );
+ _gridModel.insertByIndex( _gridModel.getCount(), column );
+ return column;
+ }
+
+ private void impl_waitForLoadedEvent()
+ {
+ synchronized( m_waitForLoad )
+ {
+ while ( !m_loaded )
+ {
+ try { m_waitForLoad.wait(); }
+ catch( InterruptedException e ) { }
+ }
+ // reset the flag for the next time
+ m_loaded = false;
+ }
+ }
+
+ /** assures that the (integer) values in the given columns of our master and detail forms are identical
+ */
+ private void verifyColumnValueIdentity( final String masterColName, final String detailColName ) throws SQLException
+ {
+ XColumnLocate locateMasterCols = UnoRuntime.queryInterface( XColumnLocate.class, m_masterForm );
+ XColumnLocate locateDetailCols = UnoRuntime.queryInterface( XColumnLocate.class, m_detailForm );
+
+ int masterValue = m_masterResult.getInt( locateMasterCols.findColumn( masterColName ) );
+ int detailValue = m_detailResult.getInt( locateDetailCols.findColumn( detailColName ) );
+
+ assure( "values in linked column pair " + detailColName + "->" + masterColName + " (" +
+ detailValue + "->" + masterValue + ") do not match (master position: " + m_masterResult.getRow() + ")!",
+ masterValue == detailValue );
+ }
+
+ public void disposing(com.sun.star.lang.EventObject eventObject)
+ {
+ }
+
+ public void loaded(com.sun.star.lang.EventObject eventObject)
+ {
+ synchronized( m_waitForLoad )
+ {
+ m_loaded = true;
+ m_waitForLoad.notify();
+ }
+ }
+
+ public void reloaded(com.sun.star.lang.EventObject eventObject)
+ {
+ synchronized( m_waitForLoad )
+ {
+ m_loaded = true;
+ m_waitForLoad.notify();
+ }
+ }
+
+ public void reloading(com.sun.star.lang.EventObject eventObject)
+ {
+ }
+
+ public void unloaded(com.sun.star.lang.EventObject eventObject)
+ {
+ }
+
+ public void unloading(com.sun.star.lang.EventObject eventObject)
+ {
+ }
+}
diff --git a/forms/qa/integration/forms/NumericValidator.java b/forms/qa/integration/forms/NumericValidator.java
new file mode 100644
index 000000000..06370cfb3
--- /dev/null
+++ b/forms/qa/integration/forms/NumericValidator.java
@@ -0,0 +1,68 @@
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+package integration.forms;
+
+public class NumericValidator extends integration.forms.ControlValidator
+{
+
+ public String explainInvalid( Object Value )
+ {
+ try
+ {
+ double value = ((Double)Value).doubleValue();
+ if ( Double.compare( Double.NaN, value ) == 0 )
+ return "This is NotANumber";
+ if ( !isProperRange( value ) )
+ return "The value must be between 0 and 100";
+ if ( !isProperDigitCount( value ) )
+ return "The value must have at most one decimal digit";
+ }
+ catch( java.lang.Exception e )
+ {
+ return "This is no valid number";
+ }
+ return "";
+ }
+
+ public boolean isValid( Object Value )
+ {
+ try
+ {
+ double value = ((Double)Value).doubleValue();
+ if ( Double.compare( Double.NaN, value ) == 0 )
+ return false;
+ if ( !isProperRange( value ) )
+ return false;
+ return isProperDigitCount( value );
+ }
+ catch( java.lang.Exception e )
+ {
+ }
+ return false;
+ }
+
+ private boolean isProperRange( double value)
+ {
+ return ( value >= 0 ) && ( value <= 100 );
+ }
+
+ private boolean isProperDigitCount( double value)
+ {
+ return ( Math.floor( value * 10 ) == value * 10 );
+ }
+}
diff --git a/forms/qa/integration/forms/RadioButtons.java b/forms/qa/integration/forms/RadioButtons.java
new file mode 100644
index 000000000..4b6e5de6f
--- /dev/null
+++ b/forms/qa/integration/forms/RadioButtons.java
@@ -0,0 +1,426 @@
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+package integration.forms;
+
+import com.sun.star.uno.*;
+import com.sun.star.util.*;
+import com.sun.star.lang.*;
+import com.sun.star.container.*;
+import com.sun.star.beans.*;
+import com.sun.star.awt.XRadioButton;
+import java.util.Arrays;
+
+public class RadioButtons extends complexlib.ComplexTestCase
+{
+ private DocumentHelper m_document; /// our current test document
+ private FormLayer m_formLayer; /// quick access to the form layer
+ private XMultiServiceFactory m_orb; /// our service factory
+ private XPropertySet m_primaryForm; /// the primary form, to be used in text documents and in the first page of spreadsheets
+ private XPropertySet m_secondaryForm; /// the secondary form, to be used in the second page of spreadsheets
+
+ /* ------------------------------------------------------------------ */
+ @Override
+ public String[] getTestMethodNames()
+ {
+ return new String[] {
+ "checkSingleButtons",
+ "checkThreeGroups",
+ "checkMultipleForms",
+ "checkCalcPageSwitch"
+ };
+ }
+
+ /* ------------------------------------------------------------------ */
+ @Override
+ public String getTestObjectName()
+ {
+ return "Form Radio Buttons Test";
+ }
+
+ /* ------------------------------------------------------------------ */
+ public void before() throws java.lang.Exception
+ {
+ m_orb = param.getMSF();
+ }
+
+ /* ------------------------------------------------------------------ */
+ private XPropertySet insertRadio( int nXPos, int nYPos, String label, String name, String refValue ) throws com.sun.star.uno.Exception, java.lang.Exception
+ {
+ return insertRadio( nXPos, nYPos, label, name, refValue, null );
+ }
+
+ /* ------------------------------------------------------------------ */
+ private XPropertySet insertRadio( int nXPos, int nYPos, String label, String name, String refValue, XPropertySet parentForm ) throws com.sun.star.uno.Exception, java.lang.Exception
+ {
+ XIndexContainer parentContainer = dbfTools.queryIndexContainer( parentForm );
+ XPropertySet xRadio = m_formLayer.createControlAndShape( "DatabaseRadioButton", nXPos, nYPos, 25, 6, parentContainer );
+ xRadio.setPropertyValue( "Label", label );
+ xRadio.setPropertyValue( "RefValue", refValue );
+ xRadio.setPropertyValue( "Name", name );
+
+ if ( null == m_primaryForm )
+ m_primaryForm = (XPropertySet)dbfTools.getParent( xRadio, XPropertySet.class );
+
+ return xRadio;
+ }
+
+ /* ------------------------------------------------------------------ */
+ /** this checks whether n groups of radio buttons, consisting of only one button each,
+ * behave properly
+ */
+ public void checkSingleButtons() throws com.sun.star.uno.Exception, java.lang.Exception
+ {
+ prepareTestStep( false );
+
+ insertRadio( 20, 30, "group 1", "group 1", "" );
+ insertRadio( 20, 38, "group 2", "group 2", "" );
+ insertRadio( 20, 46, "group 3", "group 3", "" );
+ insertRadio( 20, 54, "group 4", "group 4", "" );
+
+ // switch to alive mode
+ m_document.getCurrentView( ).toggleFormDesignMode( );
+
+ checkRadio( "group 1", "" );
+ verifySingleRadios( 1, 0, 0, 0 );
+
+ checkRadio( "group 4", "" );
+ verifySingleRadios( 1, 0, 0, 1 );
+
+ checkRadio( "group 2", "" );
+ verifySingleRadios( 1, 1, 0, 1 );
+
+ checkRadio( "group 3", "" );
+ verifySingleRadios( 1, 1, 1, 1 );
+
+ cleanupTestStep();
+ }
+
+ /* ------------------------------------------------------------------ */
+ /** creates three groups of radio buttons in a sample document, and checks whether they're working
+ */
+ public void checkThreeGroups( ) throws com.sun.star.uno.Exception, java.lang.Exception
+ {
+ prepareTestStep( false );
+
+ insertRadio( 20, 30, "group 1 (a)", "group 1", "a" );
+ insertRadio( 20, 38, "group 1 (b)", "group 1", "b" );
+
+ insertRadio( 20, 50, "group 2 (a)", "group 2", "a" );
+ insertRadio( 20, 58, "group 2 (b)", "group 2", "b" );
+
+ insertRadio( 20, 70, "group 3 (a)", "group 3", "a" );
+ insertRadio( 20, 78, "group 3 (b)", "group 3", "b" );
+
+ // switch to alive mode
+ m_document.getCurrentView( ).toggleFormDesignMode( );
+
+ // initially, after switching to alive mode, all buttons should be unchecked
+ verifySixPack( 0, 0, 0, 0, 0, 0 );
+
+ // check one button in every group
+ checkRadio( "group 1", "a" );
+ checkRadio( "group 2", "b" );
+ checkRadio( "group 3", "a" );
+ // and verify that this worked
+ verifySixPack( 1, 0, 0, 1, 1, 0 );
+
+ // check all buttons which are currently unchecked
+ checkRadio( "group 1", "b" );
+ checkRadio( "group 2", "a" );
+ checkRadio( "group 3", "b" );
+ // this should have reset the previous checks in the respective groups
+ verifySixPack( 0, 1, 1, 0, 0, 1 );
+
+ // and back to the previous check state
+ checkRadio( "group 1", "a" );
+ checkRadio( "group 2", "b" );
+ checkRadio( "group 3", "a" );
+ verifySixPack( 1, 0, 0, 1, 1, 0 );
+
+ cleanupTestStep();
+ }
+
+ /* ------------------------------------------------------------------ */
+ /** tests whether radio buttons which belong to different forms behave properly
+ */
+ public void checkMultipleForms( ) throws com.sun.star.uno.Exception, java.lang.Exception
+ {
+ prepareTestStep( false );
+
+ insertRadio( 20, 30, "group 1 (a)", "group 1", "a" );
+ insertRadio( 20, 38, "group 1 (b)", "group 1", "b" );
+ insertRadio( 20, 46, "group 1 (c)", "group 1", "c" );
+
+ m_secondaryForm = dbfTools.queryPropertySet( m_document.createSiblingForm( m_primaryForm, "secondary" ) );
+
+ insertRadio( 70, 30, "group 2 (a)", "group 2", "a", m_secondaryForm );
+ insertRadio( 70, 38, "group 2 (b)", "group 2", "b", m_secondaryForm );
+ insertRadio( 70, 46, "group 2 (c)", "group 2", "c", m_secondaryForm );
+
+ // switch to alive mode
+ m_document.getCurrentView( ).toggleFormDesignMode( );
+
+ // play around with different check states
+ checkRadio( "group 1", "b", m_primaryForm );
+ checkRadio( "group 2", "c", m_secondaryForm );
+ verifyTwoFormRadios( 0, 1, 0, 0, 0, 1 );
+
+ checkRadio( "group 1", "c", m_primaryForm );
+ verifyTwoFormRadios( 0, 0, 1, 0, 0, 1 );
+
+ checkRadio( "group 2", "a", m_secondaryForm );
+ verifyTwoFormRadios( 0, 0, 1, 1, 0, 0 );
+
+ checkRadio( "group 1", "a", m_primaryForm );
+ verifyTwoFormRadios( 1, 0, 0, 1, 0, 0 );
+
+ checkRadio( "group 2", "b", m_secondaryForm );
+ verifyTwoFormRadios( 1, 0, 0, 0, 1, 0 );
+
+ cleanupTestStep();
+ }
+
+ /* ------------------------------------------------------------------ */
+ /** tests for a special bug which we once had, where radio buttons lost their state after
+ * switching spreadsheet pages
+ */
+ public void checkCalcPageSwitch( ) throws com.sun.star.uno.Exception, java.lang.Exception
+ {
+ prepareTestStep( true );
+
+ m_formLayer.setInsertPage( 0 );
+ insertRadio( 15, 20, "group 1 (a)", "group 1", "a" );
+ insertRadio( 15, 26, "group 1 (b)", "group 1", "b" );
+
+ m_formLayer.setInsertPage( 1 );
+ XPropertySet xRadio = insertRadio( 15, 20, "group 2 (a)", "group 2", "a" );
+ insertRadio( 15, 26, "group 2 (b)", "group 2", "b" );
+ m_secondaryForm = (XPropertySet)dbfTools.getParent( xRadio, XPropertySet.class );
+
+ // switch to alive mode
+ SpreadsheetView view = (SpreadsheetView)m_document.getCurrentView( );
+ view.toggleFormDesignMode( );
+ // and do initial checking
+ checkRadio( "group 1", "a", m_primaryForm );
+ view.activateSheet( 1 );
+ checkRadio( "group 2", "b", m_secondaryForm );
+
+ // see whether the check states on the first page survived the page switch
+ verifySheetRadios( 1, 0, 0, 1 );
+ // switch back to the first sheet, and see whether the check states survived
+ view.activateSheet( 0 );
+ verifySheetRadios( 1, 0, 0, 1 );
+ // and for completely, check again after switching to third sheet and back to the first
+ view.activateSheet( 2 );
+ view.activateSheet( 1 );
+ verifySheetRadios( 1, 0, 0, 1 );
+
+ cleanupTestStep();
+ }
+
+ /* ------------------------------------------------------------------ */
+ public void after()
+ {
+ closeDocument();
+ }
+
+ /* ------------------------------------------------------------------ */
+ /** closes our document, if we have an open one
+ */
+ private void closeDocument()
+ {
+ try
+ {
+ // close our document
+ if ( m_document != null )
+ {
+ XCloseable closeDoc = UnoRuntime.queryInterface( XCloseable.class,
+ m_document.getDocument() );
+ closeDoc.close( true );
+ }
+ }
+ catch ( com.sun.star.uno.Exception e )
+ {
+ }
+ }
+
+ /* ------------------------------------------------------------------ */
+ private void prepareTestStep( boolean useSpreadsheetDocument ) throws com.sun.star.uno.Exception, java.lang.Exception
+ {
+ m_primaryForm = null;
+
+ m_document = useSpreadsheetDocument ? new SpreadsheetDocument( m_orb ) : DocumentHelper.blankTextDocument( m_orb );
+ m_formLayer = new FormLayer( m_document );
+ }
+
+ /* ------------------------------------------------------------------ */
+ private void cleanupTestStep( )
+ {
+ closeDocument();
+ }
+
+ /* ------------------------------------------------------------------ */
+ /** checks or unchecks the radio button (in our primary form) with the given name and the given ref value
+ */
+ private void checkRadio( String groupName, String refValue ) throws com.sun.star.uno.Exception, java.lang.Exception
+ {
+ checkRadio( groupName, refValue, m_primaryForm );
+ }
+
+ /* ------------------------------------------------------------------ */
+ /** checks or unchecks the radio button with the given name and the given ref value
+ */
+ private void checkRadio( String groupName, String refValue, XPropertySet form ) throws com.sun.star.uno.Exception, java.lang.Exception
+ {
+ XPropertySet xRadio = getRadioModel( groupName, refValue, form );
+
+ XRadioButton radioButton = UnoRuntime.queryInterface(
+ XRadioButton.class, m_document.getCurrentView().getControl( xRadio ) );
+ radioButton.setState( true );
+ }
+
+ /* ------------------------------------------------------------------ */
+ private XPropertySet getRadioModel( String name, String refValue ) throws com.sun.star.uno.Exception, java.lang.Exception
+ {
+ return getRadioModel( name, refValue, m_primaryForm );
+ }
+
+ /* ------------------------------------------------------------------ */
+ private XPropertySet getRadioModel( String name, String refValue, XPropertySet form ) throws com.sun.star.uno.Exception, java.lang.Exception
+ {
+ return m_formLayer.getRadioModelByRefValue( form, name, refValue );
+ }
+
+ /* ------------------------------------------------------------------ */
+ /** verifies a number of radio buttons for their states
+ */
+ private boolean verifyRadios( XPropertySet[] radios, short[] expectedStates, String errorMessage ) throws com.sun.star.uno.Exception
+ {
+ short[] actualStates = new short[radios.length];
+
+ // collect all current states. This is just to be able to emit them, in case of a failure
+ for ( int i = 0; i<radios.length; ++i )
+ {
+ actualStates[i] = ((Short)radios[i].getPropertyValue( "State" )).shortValue();
+ }
+
+ // now actually check the states
+ for ( int i = 0; i<radios.length; ++i )
+ {
+ if ( actualStates[i] != expectedStates[i] )
+ {
+ failed( errorMessage + " (expected: " + Arrays.toString( expectedStates ) + ", found: " + Arrays.toString( actualStates ) + ")" );
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ /* ------------------------------------------------------------------ */
+ /** verifies the states of the 4 radio buttons from the checkSingleButtons test
+ */
+ private boolean verifySingleRadios( int state1, int state2, int state3, int state4 ) throws com.sun.star.uno.Exception, java.lang.Exception
+ {
+ XPropertySet[] radios = new XPropertySet[4];
+ radios[0] = getRadioModel( "group 1", "" );
+ radios[1] = getRadioModel( "group 2", "" );
+ radios[2] = getRadioModel( "group 3", "" );
+ radios[3] = getRadioModel( "group 4", "" );
+
+ short[] states = new short[4];
+ states[0] = (short)state1;
+ states[1] = (short)state2;
+ states[2] = (short)state3;
+ states[3] = (short)state4;
+
+ return verifyRadios( radios, states, "single-group radio buttons do not work!" );
+ }
+
+ /* ------------------------------------------------------------------ */
+ /** verifies the states of 6 radio buttons form the checkThreeGroups test
+ */
+ private boolean verifySixPack( XPropertySet[] radios, String errorMessage,
+ int state1, int state2, int state3, int state4, int state5, int state6 ) throws com.sun.star.uno.Exception, java.lang.Exception
+ {
+ short[] states = new short[6];
+ states[0] = (short)state1;
+ states[1] = (short)state2;
+ states[2] = (short)state3;
+ states[3] = (short)state4;
+ states[4] = (short)state5;
+ states[5] = (short)state6;
+
+ return verifyRadios( radios, states, errorMessage );
+ }
+
+ /* ------------------------------------------------------------------ */
+ /** verifies the states of 6 radio buttons
+ */
+ private boolean verifySixPack( int state1, int state2, int state3, int state4, int state5, int state6 ) throws com.sun.star.uno.Exception, java.lang.Exception
+ {
+ XPropertySet[] radios = new XPropertySet[6];
+ radios[0] = getRadioModel( "group 1", "a" );
+ radios[1] = getRadioModel( "group 1", "b" );
+ radios[2] = getRadioModel( "group 2", "a" );
+ radios[3] = getRadioModel( "group 2", "b" );
+ radios[4] = getRadioModel( "group 3", "a" );
+ radios[5] = getRadioModel( "group 3", "b" );
+
+ return verifySixPack( radios, "six radio buttons, forming three different groups, do not properly work!",
+ state1, state2, state3, state4, state5, state6 );
+ }
+
+ /* ------------------------------------------------------------------ */
+ /** verifies the states of the 6 radio buttons in our checkMultipleForms test
+ */
+ private boolean verifyTwoFormRadios( int state1, int state2, int state3, int state4, int state5, int state6 ) throws com.sun.star.uno.Exception, java.lang.Exception
+ {
+ XPropertySet[] radios = new XPropertySet[6];
+ radios[0] = getRadioModel( "group 1", "a", m_primaryForm );
+ radios[1] = getRadioModel( "group 1", "b", m_primaryForm );
+ radios[2] = getRadioModel( "group 1", "c", m_primaryForm );
+ radios[3] = getRadioModel( "group 2", "a", m_secondaryForm );
+ radios[4] = getRadioModel( "group 2", "b", m_secondaryForm );
+ radios[5] = getRadioModel( "group 2", "c", m_secondaryForm );
+
+ return verifySixPack( radios, "radio buttons on different forms do not work properly!",
+ state1, state2, state3, state4, state5, state6 );
+ }
+
+ /* ------------------------------------------------------------------ */
+ /** verifies the states of the 4 radio buttons in our spreadsheet document (checkCalcPageSwitch)
+ */
+ private boolean verifySheetRadios( int state1, int state2, int state3, int state4 ) throws com.sun.star.uno.Exception, java.lang.Exception
+ {
+ XPropertySet[] radios = new XPropertySet[4];
+ radios[0] = getRadioModel( "group 1", "a", m_primaryForm );
+ radios[1] = getRadioModel( "group 1", "b", m_primaryForm );
+ radios[2] = getRadioModel( "group 2", "a", m_secondaryForm );
+ radios[3] = getRadioModel( "group 2", "b", m_secondaryForm );
+
+ short[] states = new short[4];
+ states[0] = (short)state1;
+ states[1] = (short)state2;
+ states[2] = (short)state3;
+ states[3] = (short)state4;
+
+ return verifyRadios( radios, states, "seems some of the radio button check states didn't survive the page activation(s)!" );
+ }
+}
+
diff --git a/forms/qa/integration/forms/SingleControlValidation.java b/forms/qa/integration/forms/SingleControlValidation.java
new file mode 100644
index 000000000..0873d34ac
--- /dev/null
+++ b/forms/qa/integration/forms/SingleControlValidation.java
@@ -0,0 +1,170 @@
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+package integration.forms;
+
+import com.sun.star.uno.*;
+import com.sun.star.beans.*;
+import com.sun.star.form.validation.*;
+
+public class SingleControlValidation implements XFormComponentValidityListener
+{
+ private final DocumentHelper m_document; /// our current test document
+ private final FormLayer m_formLayer; /// quick access to the form layer
+
+ private XPropertySet m_inputField;
+ private XPropertySet m_inputLabel;
+ private XPropertySet m_statusField;
+ private XPropertySet m_explanationField;
+ private final XValidator m_validator;
+
+ /* ------------------------------------------------------------------ */
+ public SingleControlValidation( DocumentHelper document, int columnPos, int rowPos, String formComponentService, XValidator validator )
+ {
+ m_document = document;
+ m_validator = validator;
+ m_formLayer = new FormLayer( m_document );
+ createControls( columnPos, rowPos, formComponentService, 1, 0 );
+ }
+
+ /* ------------------------------------------------------------------ */
+ public SingleControlValidation( DocumentHelper document, int columnPos, int rowPos, String formComponentService, XValidator validator, int controlCount, int controlHeight )
+ {
+ m_document = document;
+ m_validator = validator;
+ m_formLayer = new FormLayer( m_document );
+ createControls( columnPos, rowPos, formComponentService, controlCount, controlHeight );
+ }
+
+ /* ------------------------------------------------------------------ */
+ public XPropertySet getInputField()
+ {
+ return m_inputField;
+ }
+
+ /* ------------------------------------------------------------------ */
+ public void setExplanatoryText( String text )
+ {
+ try
+ {
+ m_inputLabel.setPropertyValue( "Label", text );
+ }
+ catch( com.sun.star.uno.Exception e )
+ {
+ e.printStackTrace( System.err );
+ }
+ }
+
+ /* ------------------------------------------------------------------ */
+ private void createControls( int columnPos, int rowPos, String formComponentService, int controlCount, int controlHeight )
+ {
+ try
+ {
+ m_inputLabel = m_formLayer.createControlAndShape( "FixedText", columnPos, rowPos, 70, 12, null );
+ m_inputLabel.setPropertyValue( "MultiLine", Boolean.TRUE );
+
+ com.sun.star.awt.FontDescriptor font = (com.sun.star.awt.FontDescriptor)m_inputLabel.getPropertyValue( "FontDescriptor" );
+ font.Weight = com.sun.star.awt.FontWeight.BOLD;
+ m_inputLabel.setPropertyValue( "FontDescriptor", font );
+
+ if ( controlHeight == 0 )
+ controlHeight = 6;
+
+ int controlPos = rowPos + 12;
+ XPropertySet[] controls = new XPropertySet[ controlCount ];
+ for ( int i = 0; i < controlCount; ++i, controlPos += controlHeight )
+ {
+ controls[ i ] = m_formLayer.createControlAndShape( formComponentService, columnPos, controlPos, 25, controlHeight, null );
+ controls[ i ].setPropertyValue( "Name", formComponentService );
+ controls[ i ].setPropertyValue( "Tag", String.valueOf( i ) );
+
+ if ( controls[ i ].getPropertySetInfo().hasPropertyByName( "Border" ) )
+ controls[ i ].setPropertyValue( "Border", Short.valueOf( (short)2 ) );
+
+ XValidatableFormComponent xComp = UnoRuntime.queryInterface( XValidatableFormComponent.class,
+ controls[ i ] );
+ xComp.addFormComponentValidityListener( this );
+ }
+ m_inputField = controls[ 0 ];
+
+
+ controlPos += 4;
+ XPropertySet xLabel = m_formLayer.createControlAndShape( "FixedText", columnPos, controlPos, 70, 4, null );
+ xLabel.setPropertyValue( "Label", "Status:" );
+ controlPos += 4;
+ m_statusField = m_formLayer.createControlAndShape( "FixedText", columnPos, controlPos, 70, 4, null );
+ m_statusField.setPropertyValue( "Label", "" );
+
+
+ controlPos += 6;
+ xLabel = m_formLayer.createControlAndShape( "FixedText", columnPos, controlPos, 70, 4, null );
+ xLabel.setPropertyValue( "Label", "Explanation for invalidity:" );
+ controlPos += 4;
+ m_explanationField = m_formLayer.createControlAndShape( "FixedText", columnPos, controlPos, 70, 4, null );
+ m_explanationField.setPropertyValue( "Label", "" );
+
+ XValidatable xValidatable = UnoRuntime.queryInterface( XValidatable.class, m_inputField );
+ xValidatable.setValidator( m_validator );
+ }
+ catch( java.lang.Exception e )
+ {
+ e.printStackTrace( System.err );
+ }
+ }
+
+ /* ------------------------------------------------------------------ */
+ /* XEventListener overridables */
+ /* ------------------------------------------------------------------ */
+ public void disposing( com.sun.star.lang.EventObject eventObject )
+ {
+ // not interested in
+ }
+
+ /* ------------------------------------------------------------------ */
+ /* XFormComponentValidityListener overridables */
+ /* ------------------------------------------------------------------ */
+ public void componentValidityChanged( com.sun.star.lang.EventObject eventObject )
+ {
+ try
+ {
+ if ( m_inputField.equals( eventObject.Source ) )
+ {
+ XValidatableFormComponent xComp = UnoRuntime.queryInterface( XValidatableFormComponent.class,
+ eventObject.Source );
+ // the current value
+ Object value = xComp.getCurrentValue();
+
+ // the current validity flag
+ boolean isValid = xComp.isValid();
+
+ m_statusField.setPropertyValue("Label", isValid ? "valid" : "invalid" );
+ m_statusField.setPropertyValue( "TextColor", Integer.valueOf( isValid ? 0x008000 : 0x800000 ) );
+
+ String validityMessage = "";
+ if ( !isValid )
+ validityMessage = m_validator.explainInvalid( value );
+ m_explanationField.setPropertyValue( "Label", validityMessage );
+ }
+ }
+ catch( com.sun.star.uno.Exception e )
+ {
+ e.printStackTrace( System.err );
+ }
+ }
+
+}
diff --git a/forms/qa/integration/forms/SpreadsheetDocument.java b/forms/qa/integration/forms/SpreadsheetDocument.java
new file mode 100644
index 000000000..2db38ab92
--- /dev/null
+++ b/forms/qa/integration/forms/SpreadsheetDocument.java
@@ -0,0 +1,68 @@
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+package integration.forms;
+
+import com.sun.star.uno.*;
+import com.sun.star.lang.XMultiServiceFactory;
+import com.sun.star.lang.XComponent;
+import com.sun.star.table.XCellRange;
+import com.sun.star.container.XIndexAccess;
+import com.sun.star.sheet.XSpreadsheetDocument;
+import com.sun.star.sheet.XSpreadsheets;
+
+public class SpreadsheetDocument extends DocumentHelper
+{
+ /** Creates a new blank spreadsheet document */
+ /* ------------------------------------------------------------------ */
+ public SpreadsheetDocument( XMultiServiceFactory orb ) throws com.sun.star.uno.Exception
+ {
+ super( orb, implLoadAsComponent( orb, "private:factory/scalc" ) );
+ }
+
+ /* ------------------------------------------------------------------ */
+ public SpreadsheetDocument( XMultiServiceFactory orb, XComponent document )
+ {
+ super( orb, document );
+ }
+
+ /* ------------------------------------------------------------------ */
+ /** returns the sheets collection
+ */
+ public XSpreadsheets getSheets()
+ {
+ XSpreadsheetDocument spreadsheetDoc = UnoRuntime.queryInterface( XSpreadsheetDocument.class,
+ getDocument()
+ );
+ return spreadsheetDoc.getSheets();
+ }
+
+ /* ------------------------------------------------------------------ */
+ /** returns the sheet with the given index
+ */
+ public XCellRange getSheet( int index ) throws com.sun.star.uno.Exception
+ {
+ XIndexAccess sheets = UnoRuntime.queryInterface( XIndexAccess.class,
+ getSheets()
+ );
+ return UnoRuntime.queryInterface( XCellRange.class,
+ sheets.getByIndex( index )
+ );
+ }
+
+}
diff --git a/forms/qa/integration/forms/SpreadsheetView.java b/forms/qa/integration/forms/SpreadsheetView.java
new file mode 100644
index 000000000..22ff1c06b
--- /dev/null
+++ b/forms/qa/integration/forms/SpreadsheetView.java
@@ -0,0 +1,59 @@
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+package integration.forms;
+
+import com.sun.star.uno.*;
+import com.sun.star.lang.*;
+import com.sun.star.frame.*;
+import com.sun.star.sheet.*;
+import com.sun.star.container.*;
+
+public class SpreadsheetView extends integration.forms.DocumentViewHelper
+{
+
+ /** Creates a new instance of SpreadsheetView */
+ public SpreadsheetView( XMultiServiceFactory orb, DocumentHelper document, XController controller )
+ {
+ super( orb, document, controller );
+ }
+
+ /** activates the sheet with the given index
+ */
+ void activateSheet( int sheetIndex )
+ {
+ try
+ {
+ // get the sheet to activate
+ XSpreadsheetDocument doc = UnoRuntime.queryInterface(
+ XSpreadsheetDocument.class, getDocument().getDocument() );
+ XIndexAccess sheets = UnoRuntime.queryInterface(
+ XIndexAccess.class, doc.getSheets() );
+
+ XSpreadsheet sheet = UnoRuntime.queryInterface(
+ XSpreadsheet.class, sheets.getByIndex( sheetIndex ) );
+
+ // activate
+ XSpreadsheetView view = UnoRuntime.queryInterface(
+ XSpreadsheetView.class, getController() );
+ view.setActiveSheet( sheet );
+ }
+ catch( com.sun.star.uno.Exception e )
+ {
+ }
+ }
+}
diff --git a/forms/qa/integration/forms/TableCellTextBinding.java b/forms/qa/integration/forms/TableCellTextBinding.java
new file mode 100644
index 000000000..7f745a8f7
--- /dev/null
+++ b/forms/qa/integration/forms/TableCellTextBinding.java
@@ -0,0 +1,192 @@
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+package integration.forms;
+
+import com.sun.star.uno.Type;
+import com.sun.star.uno.UnoRuntime;
+import com.sun.star.table.XCell;
+import com.sun.star.util.XModifyListener;
+import com.sun.star.text.XTextRange;
+
+/** a value binding to be connected to a form control
+
+ This binding synchronizes the text contained in a table cell (which you must
+ pass upon construction) to the text in an XBindableValue.
+
+ Well, in real it does not synchronize both directions. The ValueBinding
+ service has not much room for own activity: It allows notification of changes
+ in the own value, and it allows external instances to set the current value.
+
+ Note that we implement this binding as a separate thread, which is (more or
+ less permanently) polling for a new text at the cell. This is unfortunate, but
+ sadly the Writer table cells do not support actively notifying changes in their
+ content to other interested parties.
+*/
+public class TableCellTextBinding
+ extends java.lang.Thread
+ implements com.sun.star.form.binding.XValueBinding,
+ com.sun.star.util.XModifyBroadcaster
+{
+ private final XTextRange m_cellText;
+ private Object m_writeSignal;
+ private String m_newCellText;
+ private String m_lastKnownCellText;
+ private boolean m_haveNewCellText;
+ private final java.util.List<com.sun.star.util.XModifyListener> m_listeners;
+
+ /** Creates a new instance of TableCellTextBinding */
+ public TableCellTextBinding( XCell cell )
+ {
+ m_cellText = UnoRuntime.queryInterface( XTextRange.class, cell );
+
+ m_newCellText = "";
+ m_listeners = new java.util.LinkedList<com.sun.star.util.XModifyListener>();
+
+ start();
+ }
+
+ /** retrieves the list of data types which this binding can exchange
+ */
+ public com.sun.star.uno.Type[] getSupportedValueTypes()
+ {
+ try
+ {
+ // well, only strings here ...
+ return new Type[] {
+ getStringType()
+ };
+ }
+ catch( java.lang.Exception e )
+ {
+ }
+ return new Type[] { };
+ }
+
+ /** retrieves the current value
+ */
+ public Object getValue(com.sun.star.uno.Type type) throws com.sun.star.form.binding.IncompatibleTypesException
+ {
+ if ( !type.equals( getStringType() ) )
+ throw new com.sun.star.form.binding.IncompatibleTypesException();
+
+ return m_cellText.getString();
+ }
+
+ /** sets a new value
+ */
+ public void setValue(Object obj) throws com.sun.star.form.binding.IncompatibleTypesException
+ {
+ String text;
+ try
+ {
+ text = (String)obj;
+ }
+ catch( java.lang.ClassCastException e )
+ {
+ throw new com.sun.star.form.binding.IncompatibleTypesException();
+ }
+ // remember the new text
+ // and wake up the thread which is waiting for it
+ synchronized( m_writeSignal )
+ {
+ m_newCellText = text;
+ m_haveNewCellText = true;
+ m_writeSignal.notify();
+ }
+ }
+
+ /** determines whether a given value type is supported
+ */
+ public boolean supportsType(com.sun.star.uno.Type type)
+ {
+ return type.equals( getStringType() );
+ }
+
+ /** retrieves the UNO type for the string class
+ */
+ private static final Type getStringType()
+ {
+ return new com.sun.star.uno.Type( String.class );
+ }
+
+ /** runs the thread
+ */
+ @Override
+ public void run()
+ {
+ try
+ {
+ m_writeSignal = new Object();
+ while ( true )
+ {
+ // go sleep a while
+ synchronized( m_writeSignal )
+ {
+ m_writeSignal.wait( 200 );
+ // if there's new text in the control, propagate it to the cell
+ if ( m_haveNewCellText )
+ {
+ m_cellText.setString( m_newCellText );
+ m_lastKnownCellText = m_newCellText;
+ }
+ m_haveNewCellText = false;
+ }
+
+ // if there's new text in the cell, propagate it to the control
+ String currentCellText = m_cellText.getString();
+ if ( !currentCellText.equals( m_lastKnownCellText ) )
+ {
+ m_lastKnownCellText = currentCellText;
+ // notify the modification
+ synchronized( m_listeners )
+ {
+ com.sun.star.lang.EventObject eventSource = new com.sun.star.lang.EventObject( this );
+
+ java.util.Iterator loop = m_listeners.iterator();
+ while ( loop.hasNext() )
+ {
+ ((XModifyListener)loop.next()).modified( eventSource );
+ }
+ }
+ }
+ }
+ }
+ catch( java.lang.Exception e )
+ {
+ e.printStackTrace(System.err);
+ }
+ }
+
+ public void addModifyListener(com.sun.star.util.XModifyListener xModifyListener)
+ {
+ synchronized( m_listeners )
+ {
+ m_listeners.add( xModifyListener );
+ }
+ }
+
+ public void removeModifyListener(com.sun.star.util.XModifyListener xModifyListener)
+ {
+ synchronized( m_listeners )
+ {
+ m_listeners.remove( xModifyListener );
+ }
+ }
+
+}
diff --git a/forms/qa/integration/forms/TestCase.java b/forms/qa/integration/forms/TestCase.java
new file mode 100644
index 000000000..cc1e6ec62
--- /dev/null
+++ b/forms/qa/integration/forms/TestCase.java
@@ -0,0 +1,150 @@
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+package integration.forms;
+
+import com.sun.star.lang.XMultiServiceFactory;
+import com.sun.star.util.XCloseable;
+import com.sun.star.util.XModifiable;
+
+public abstract class TestCase extends complexlib.ComplexTestCase implements com.sun.star.lang.XEventListener
+{
+ protected XMultiServiceFactory m_orb; /// our service factory
+ private DocumentType m_documentType; /// the type of our document
+ protected DocumentHelper m_document; /// our current test document
+ protected FormLayer m_formLayer;
+
+ /** Creates a new instance of TestCase */
+ public TestCase( DocumentType docType )
+ {
+ m_documentType = docType;
+ }
+
+ /* ------------------------------------------------------------------ */
+ @Override
+ public String getTestObjectName()
+ {
+ return this.getClass().getName();
+ }
+
+ /* ------------------------------------------------------------------ */
+ public void before() throws java.lang.Exception
+ {
+ m_orb = param.getMSF();
+ }
+
+ /* ------------------------------------------------------------------ */
+ public void after() throws java.lang.Exception
+ {
+ }
+
+ /* ------------------------------------------------------------------ */
+ /** closes our document, if we have an open one, via (simulated) user input
+ */
+ protected void closeDocumentByUI()
+ {
+ try
+ {
+ if ( m_document != null )
+ {
+ // first, set the document to "unmodified"
+ XModifiable docModify = m_document.query( XModifiable.class );
+ docModify.setModified( false );
+
+ m_document.getCurrentView().dispatch( ".uno:CloseDoc" );
+
+ // CloseDoc is asynchronous, so wait until it's done - or 1 second, at most
+ synchronized ( this ) { wait( 1000 ); }
+ }
+ }
+ catch ( java.lang.Exception e )
+ {
+ e.printStackTrace( System.err );
+ }
+ }
+
+ /* ------------------------------------------------------------------ */
+ /** closes our document, if we have an open one
+ */
+ protected void closeDocument()
+ {
+ try
+ {
+ // close our document
+ if ( m_document != null )
+ {
+ XCloseable closeDoc = m_document.query( XCloseable.class );
+ closeDoc.close( true );
+ }
+ }
+ catch ( com.sun.star.uno.Exception e )
+ {
+ e.printStackTrace( System.err );
+ }
+ }
+
+ /* ------------------------------------------------------------------ */
+ /** prepares a new document to work with
+ */
+ protected void prepareDocument() throws com.sun.star.uno.Exception, java.lang.Exception
+ {
+ m_document = DocumentHelper.blankDocument( m_orb, m_documentType );
+ m_document.getDocument( ).addEventListener( this );
+ m_formLayer = new FormLayer( m_document );
+ }
+
+ /* ------------------------------------------------------------------ */
+ /* internal methods */
+ /* ------------------------------------------------------------------ */
+ /** waits for the user to press a key (on the console where she started the java program)
+ or the document to be closed by the user.
+ @return
+ <TRUE/> if the user pressed a key on the console, <FALSE/> if she closed the document
+ */
+ protected boolean waitForUserInput() throws java.lang.Exception
+ {
+ synchronized (this)
+ {
+ integration.forms.WaitForInput aWait = new integration.forms.WaitForInput( this );
+ aWait.start();
+ wait();
+
+ // if the waiter thread is done, the user pressed enter
+ boolean bKeyPressed = aWait.isDone();
+ if ( !bKeyPressed )
+ aWait.interrupt();
+
+ return bKeyPressed;
+ }
+ }
+
+ /* ------------------------------------------------------------------ */
+ /* XEventListener overridables */
+ /* ------------------------------------------------------------------ */
+ public void disposing( com.sun.star.lang.EventObject eventObject )
+ {
+ if ( m_document.getDocument().equals( eventObject.Source ) )
+ {
+ // notify ourself that we can stop waiting for user input
+ synchronized (this)
+ {
+ notify();
+ }
+ }
+ }
+}
diff --git a/forms/qa/integration/forms/TextValidator.java b/forms/qa/integration/forms/TextValidator.java
new file mode 100644
index 000000000..0ed59dd41
--- /dev/null
+++ b/forms/qa/integration/forms/TextValidator.java
@@ -0,0 +1,65 @@
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+package integration.forms;
+
+public class TextValidator extends integration.forms.ControlValidator
+{
+
+ public String explainInvalid( Object Value )
+ {
+ try
+ {
+ String value = (String)Value;
+ if ( containsZs( value ) )
+ return "No Z's allowed here";
+ if ( !isProperChunks( value ) )
+ return "Need 3 * n characters";
+ }
+ catch( java.lang.Exception e )
+ {
+ return "ooops. Unknown error";
+ }
+ return "";
+ }
+
+ public boolean isValid( Object Value )
+ {
+ try
+ {
+ String value = (String)Value;
+ if ( containsZs( value ) )
+ return false;
+ return isProperChunks( value );
+ }
+ catch( java.lang.Exception e )
+ {
+ }
+ return false;
+ }
+
+ private boolean isProperChunks( String value )
+ {
+ return ( value.length() % 3 ) == 0;
+ }
+
+ private boolean containsZs( String value )
+ {
+ return ( value.indexOf( 'Z' ) != -1 )
+ || ( value.indexOf( 'z' ) != -1 );
+ }
+}
diff --git a/forms/qa/integration/forms/TimeValidator.java b/forms/qa/integration/forms/TimeValidator.java
new file mode 100644
index 000000000..8ff19464a
--- /dev/null
+++ b/forms/qa/integration/forms/TimeValidator.java
@@ -0,0 +1,73 @@
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+package integration.forms;
+
+public class TimeValidator extends integration.forms.ControlValidator
+{
+
+ public String explainInvalid( Object Value )
+ {
+ try
+ {
+ if ( isVoid( Value ) )
+ return "empty input";
+
+ com.sun.star.util.Time timeValue = (com.sun.star.util.Time)Value;
+ if ( isInvalidTime( timeValue ) )
+ return "this is no valid time";
+ if ( !isFullHour( timeValue ) )
+ return "time must denote a full hour";
+ }
+ catch( java.lang.Exception e )
+ {
+ return "this is no valid time";
+ }
+ return "";
+ }
+
+ public boolean isValid( Object Value )
+ {
+ try
+ {
+ if ( isVoid( Value ) )
+ return false;
+
+ com.sun.star.util.Time timeValue = (com.sun.star.util.Time)
+ com.sun.star.uno.AnyConverter.toObject(
+ com.sun.star.util.Time.class, Value);
+ if ( isInvalidTime( timeValue ) )
+ return false;
+ return isFullHour( timeValue );
+ }
+ catch( java.lang.Exception e )
+ {
+ e.printStackTrace( System.err );
+ }
+ return false;
+ }
+
+ private boolean isInvalidTime( com.sun.star.util.Time timeValue )
+ {
+ return ( timeValue.Hours == -1 ) && ( timeValue.Minutes == -1 ) && ( timeValue.Seconds == -1 ) && ( timeValue.NanoSeconds == -1 );
+ }
+
+ private boolean isFullHour( com.sun.star.util.Time timeValue )
+ {
+ return ( timeValue.Minutes == 0 ) && ( timeValue.Seconds == 0 ) && ( timeValue.NanoSeconds == 0 );
+ }
+}
diff --git a/forms/qa/integration/forms/ValueBinding.java b/forms/qa/integration/forms/ValueBinding.java
new file mode 100644
index 000000000..3b6e66bdc
--- /dev/null
+++ b/forms/qa/integration/forms/ValueBinding.java
@@ -0,0 +1,112 @@
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+package integration.forms;
+
+import com.sun.star.uno.UnoRuntime;
+
+import com.sun.star.beans.XPropertySet;
+import com.sun.star.text.XTextDocument;
+import com.sun.star.text.XText;
+import com.sun.star.text.XTextTable;
+import com.sun.star.text.XTextCursor;
+import com.sun.star.form.binding.XValueBinding;
+import com.sun.star.form.binding.XBindableValue;
+
+public class ValueBinding extends integration.forms.TestCase
+{
+ /** Creates a new instance of ValueBinding */
+ public ValueBinding()
+ {
+ super( DocumentType.WRITER );
+ }
+
+ public static boolean isInteractiveTest()
+ {
+ return true;
+ }
+
+ /* ------------------------------------------------------------------ */
+ @Override
+ public String[] getTestMethodNames()
+ {
+ return new String[] {
+ "checkBindingProperties"
+ };
+ }
+
+ /* ------------------------------------------------------------------ */
+ @Override
+ public String getTestObjectName()
+ {
+ return "Form Control Value Binding Test";
+ }
+
+ /* ------------------------------------------------------------------ */
+ @Override
+ public void before() throws com.sun.star.uno.Exception, java.lang.Exception
+ {
+ super.before();
+ prepareDocument();
+ }
+
+ /* ------------------------------------------------------------------ */
+ @Override
+ public void after() throws com.sun.star.uno.Exception, java.lang.Exception
+ {
+ super.waitForUserInput();
+ super.closeDocument();
+ }
+
+ /* ------------------------------------------------------------------ */
+ public void checkBindingProperties() throws java.lang.Exception
+ {
+ }
+
+ /* ------------------------------------------------------------------ */
+ @Override
+ protected void prepareDocument() throws com.sun.star.uno.Exception, java.lang.Exception
+ {
+ super.prepareDocument();
+
+ // insert a table with exactly one cell. The content of this table will be synced with
+ // the content of a form control
+ XTextDocument textDoc = UnoRuntime.queryInterface( XTextDocument.class, m_document.getDocument() );
+ XText documentText = textDoc.getText();
+ XTextCursor textCursor = documentText.createTextCursor();
+
+ XTextTable table = UnoRuntime.queryInterface( XTextTable.class,
+ m_document.createInstance( "com.sun.star.text.TextTable" )
+ );
+ table.initialize( 1, 1 );
+ documentText.insertTextContent( textCursor, table, false );
+
+ // insert our sample control
+ XPropertySet textControl = m_formLayer.insertControlLine( "DatabaseTextField", "Test", "", 10 );
+
+ // create a value binding for the first cell of the table
+ XValueBinding cellBinding = new TableCellTextBinding( table.getCellByName( "A1" ) );
+ // and bind it to the control
+ XBindableValue bindable = UnoRuntime.queryInterface(
+ XBindableValue.class, textControl
+ );
+ bindable.setValueBinding( cellBinding );
+
+ // toggle the view to alive mode
+ m_document.getCurrentView( ).toggleFormDesignMode( );
+ }
+ }
diff --git a/forms/qa/integration/forms/WaitForInput.java b/forms/qa/integration/forms/WaitForInput.java
new file mode 100644
index 000000000..c04c4dbe3
--- /dev/null
+++ b/forms/qa/integration/forms/WaitForInput.java
@@ -0,0 +1,58 @@
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+package integration.forms;
+
+class WaitForInput extends java.lang.Thread
+{
+ private final Object m_aToNotify;
+ private boolean m_bDone;
+
+ public WaitForInput( Object aToNotify )
+ {
+ m_aToNotify = aToNotify;
+ m_bDone = false;
+ }
+
+ public boolean isDone()
+ {
+ return m_bDone;
+ }
+
+ @Override
+ public void run()
+ {
+ try
+ {
+ System.out.println( "\npress enter to exit" );
+ System.in.read();
+
+ m_bDone = true;
+ // notify that the user pressed the key
+ synchronized (m_aToNotify)
+ {
+ m_aToNotify.notify();
+ }
+ }
+ catch( java.lang.Exception e )
+ {
+ // not really interested in
+ System.err.println( e );
+ }
+ }
+}
+
diff --git a/forms/qa/integration/forms/XMLFormSettings.java b/forms/qa/integration/forms/XMLFormSettings.java
new file mode 100644
index 000000000..8b393d094
--- /dev/null
+++ b/forms/qa/integration/forms/XMLFormSettings.java
@@ -0,0 +1,213 @@
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+package integration.forms;
+
+import com.sun.star.beans.PropertyValue;
+import com.sun.star.beans.XPropertySet;
+import com.sun.star.form.binding.IncompatibleTypesException;
+import com.sun.star.form.binding.XBindableValue;
+import com.sun.star.form.binding.XValueBinding;
+import com.sun.star.frame.XStorable;
+import com.sun.star.io.IOException;
+import com.sun.star.lang.XMultiServiceFactory;
+import com.sun.star.uno.UnoRuntime;
+import com.sun.star.util.CloseVetoException;
+import com.sun.star.util.XCloseable;
+import com.sun.star.xml.dom.XNode;
+import com.sun.star.xsd.DataTypeClass;
+import java.io.File;
+import org.openoffice.xforms.Instance;
+import org.openoffice.xforms.Model;
+import org.openoffice.xforms.XMLDocument;
+
+public class XMLFormSettings extends complexlib.ComplexTestCase
+{
+ private XMLDocument m_document;
+ private Model m_defaultModel;
+ private FormLayer m_formLayer;
+ private XPropertySet m_stringBinding;
+ private XPropertySet m_booleanBinding;
+ private XPropertySet m_dateBinding;
+
+ /* ------------------------------------------------------------------ */
+ @Override
+ public String[] getTestMethodNames()
+ {
+ return new String[] {
+ "checkExternalData"
+ };
+ }
+
+ /* ------------------------------------------------------------------ */
+ @Override
+ public String getTestObjectName()
+ {
+ return "Form Control Spreadsheet Cell Binding Test";
+ }
+
+ /* ------------------------------------------------------------------ */
+ public void before() throws java.lang.Exception
+ {
+ // create the document and assign related members
+ XMultiServiceFactory orb = param.getMSF();
+ m_document = new XMLDocument( orb );
+ m_formLayer = new FormLayer( m_document );
+
+ // create a simple structure in the DOM tree: an element with two attributes
+ String[] modelNames = m_document.getXFormModelNames();
+ m_defaultModel = m_document.getXFormModel( modelNames[0] );
+ final Instance defaultInstance = m_defaultModel.getDefaultInstance();
+ // remove the default root node
+ defaultInstance.removeNode( "instanceData" );
+ // create test structures
+ XNode stringElement = defaultInstance.createElement( "stringElement" );
+ XNode booleanAttrib = defaultInstance.createAttribute( stringElement, "booleanAttribute", "true" );
+ XNode dateAttrib = defaultInstance.createAttribute( stringElement, "dateAttribute" );
+
+ assure( "booleanAttrib's parent is wrong",
+ UnoRuntime.areSame( stringElement, booleanAttrib.getParentNode() ) );
+ assure( "dateAttrib's parent is wrong",
+ UnoRuntime.areSame( stringElement, dateAttrib.getParentNode() ) );
+
+ // also create bindings for the element and its attributes, of the proper type
+ m_stringBinding = m_defaultModel.createBindingForNode( stringElement, DataTypeClass.STRING );
+ m_booleanBinding = m_defaultModel.createBindingForNode( booleanAttrib, DataTypeClass.BOOLEAN );
+ m_dateBinding = m_defaultModel.createBindingForNode( dateAttrib, DataTypeClass.DATE );
+
+ // TODO: set up the bindings so that the date bindings is relevant if and only if
+ // the boolean value is true
+
+ // store the document
+ File tempFile = File.createTempFile( "xmlforms", ".odt" );
+ tempFile.deleteOnExit();
+ String fileURL = tempFile.toURI().toURL().toExternalForm();
+ XStorable store = UnoRuntime.queryInterface( XStorable.class,
+ m_document.getDocument() );
+ store.storeAsURL( fileURL, new PropertyValue[] {} );
+ assure( "document still modified after saving it", !m_document.isModified() );
+ }
+
+ /* ------------------------------------------------------------------ */
+ public void after() throws com.sun.star.uno.Exception, java.lang.Exception
+ {
+ impl_closeDocument();
+ }
+
+ /* ------------------------------------------------------------------ */
+ private void impl_closeDocument() throws CloseVetoException
+ {
+ if ( m_document != null )
+ {
+ XCloseable closeDoc = UnoRuntime.queryInterface( XCloseable.class,
+ m_document.getDocument() );
+ closeDoc.close( true );
+ }
+ }
+
+ /* ------------------------------------------------------------------ */
+ private static void impl_bind( XPropertySet _control, XPropertySet _binding ) throws IncompatibleTypesException
+ {
+ XBindableValue bindableControl = UnoRuntime.queryInterface(
+ XBindableValue.class, _control );
+ XValueBinding binding = UnoRuntime.queryInterface(
+ XValueBinding.class, _binding );
+ bindableControl.setValueBinding( binding );
+ }
+
+ /* ------------------------------------------------------------------ */
+ /** checks if master-detail relationships including multiple keys work
+ */
+ public void checkExternalData() throws com.sun.star.uno.Exception, java.lang.Exception
+ {
+ // some controls
+ XPropertySet stringControl = m_formLayer.createLabeledControl(
+ "DatabaseTextField", "Task", 10, 10, 6 );
+ impl_bind( stringControl, m_stringBinding );
+
+ XPropertySet booleanControl = m_formLayer.createControlAndShape(
+ "DatabaseCheckBox", 35, 18, 25, 6 );
+ booleanControl.setPropertyValue( "Label", "has due date" );
+ impl_bind( booleanControl, m_booleanBinding );
+
+ XPropertySet dateControl = m_formLayer.createControlAndShape(
+ "DatabaseDateField", 40, 26, 25, 6 );
+ dateControl.setPropertyValue( "Dropdown", Boolean.TRUE );
+ impl_bind( dateControl, m_dateBinding );
+
+ m_document.getCurrentView( ).toggleFormDesignMode( );
+
+ // ensure the model is set up as containing "document-internal" data
+ m_defaultModel.setIsDocumentInternalData( true );
+ assure( "setting up the document to contain 'internal data' failed",
+ m_defaultModel.getIsDocumentInternalData() );
+ impl_storeDocument();
+
+ // okay, here we go ...
+ // what this particular test is about is to check whether we can set up the model
+ // so that any changes to any controls bound to any data in this model actually marks
+ // the containing document as modified
+ m_formLayer.userTextInput( stringControl, "don't break this test" );
+ assure( "model data changed, but document is not modified",
+ m_document.isModified() );
+
+ // TODO: do this with the other control/binding types, too
+
+ // no the other way round: set up the model to contain "document-external" data
+ m_defaultModel.setIsDocumentInternalData( false );
+ assure( "setting up the document to contain 'internal data' failed",
+ !m_defaultModel.getIsDocumentInternalData() );
+ impl_storeDocument();
+
+ // and check that now, changes in the controls / model data are not reflected in
+ // document's modified state
+ m_formLayer.userTextInput( stringControl, "(or any other test, that is)" );
+ assure( "model data changed, but document is modified",
+ !m_document.isModified() );
+
+
+ // finally, check whether the flag survives loading and saving
+ Model internalDataModel = m_document.addXFormModel( "internalData" );
+ internalDataModel.setIsDocumentInternalData( true );
+ Model externalDataModel = m_document.addXFormModel( "externalData" );
+ externalDataModel.setIsDocumentInternalData( false );
+
+ impl_storeDocument();
+ m_document.reload();
+
+ internalDataModel = m_document.getXFormModel( "internalData" );
+ externalDataModel = m_document.getXFormModel( "externalData" );
+
+ assure( "setting up a model to contain 'internal data' did not survive reloading",
+ internalDataModel.getIsDocumentInternalData() );
+ assure( "setting up a model to contain 'external data' did not survive reloading",
+ !externalDataModel.getIsDocumentInternalData() );
+ }
+
+ /* ------------------------------------------------------------------ */
+ /** stores our document
+ * @throws com.sun.star.io.IOException
+ */
+ private void impl_storeDocument() throws IOException
+ {
+ XStorable store = UnoRuntime.queryInterface( XStorable.class,
+ m_document.getDocument() );
+ store.store();
+ assure( "document still modified after saving it", !m_document.isModified() );
+ }
+}
diff --git a/forms/qa/integration/forms/dbfTools.java b/forms/qa/integration/forms/dbfTools.java
new file mode 100644
index 000000000..fe61c6ef4
--- /dev/null
+++ b/forms/qa/integration/forms/dbfTools.java
@@ -0,0 +1,73 @@
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+package integration.forms;
+
+import com.sun.star.uno.*;
+import com.sun.star.lang.*;
+import com.sun.star.beans.*;
+import com.sun.star.container.*;
+
+/** provides global helpers
+*/
+class dbfTools
+{
+ /* ------------------------------------------------------------------ */
+ /** disposes the component given
+ */
+ public static void disposeComponent( Object xComp ) throws java.lang.RuntimeException
+ {
+ XComponent xComponent = queryComponent( xComp );
+ if ( null != xComponent )
+ xComponent.dispose();
+ }
+
+ /* ------------------------------------------------------------------ */
+ /** queries an object for the XPropertySet interface
+ */
+ public static XPropertySet queryPropertySet( Object aComp )
+ {
+ return UnoRuntime.queryInterface( XPropertySet.class, aComp );
+ }
+
+ /* ------------------------------------------------------------------ */
+ /** queries an object for the XIndexContainer interface
+ */
+ public static XIndexContainer queryIndexContainer( Object aComp )
+ {
+ return UnoRuntime.queryInterface( XIndexContainer.class, aComp );
+ }
+
+ /* ------------------------------------------------------------------ */
+ /** queries an object for the XComponent interface
+ */
+ public static XComponent queryComponent( Object aComp )
+ {
+ return UnoRuntime.queryInterface( XComponent.class, aComp );
+ }
+
+ /* ------------------------------------------------------------------ */
+ /** retrieves the parent of the given object
+ */
+ @SuppressWarnings("unchecked")
+ static Object getParent( Object aComponent, Class aInterfaceClass )
+ {
+ XChild xAsChild = UnoRuntime.queryInterface( XChild.class, aComponent );
+ return UnoRuntime.queryInterface( aInterfaceClass, xAsChild.getParent() );
+ }
+}