/* * 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 index * 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 ); } }