/* * 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 complex.toolkit; import com.sun.star.awt.XControl; import com.sun.star.awt.XControlContainer; import com.sun.star.awt.XControlModel; import com.sun.star.awt.XToolkit; import com.sun.star.awt.grid.DefaultGridDataModel; import com.sun.star.awt.grid.XGridColumn; import com.sun.star.awt.grid.XGridColumnModel; import com.sun.star.awt.grid.XGridControl; import com.sun.star.awt.grid.XGridDataModel; import com.sun.star.awt.grid.XMutableGridDataModel; import com.sun.star.awt.grid.XSortableMutableGridDataModel; import com.sun.star.beans.Pair; import com.sun.star.beans.XPropertySet; import com.sun.star.container.ContainerEvent; import com.sun.star.container.XContainerListener; import com.sun.star.container.XNameContainer; import com.sun.star.lang.EventObject; import com.sun.star.lang.IndexOutOfBoundsException; import com.sun.star.lang.XComponent; import com.sun.star.lang.XEventListener; import com.sun.star.lang.XMultiServiceFactory; import com.sun.star.uno.Exception; import com.sun.star.uno.UnoRuntime; import com.sun.star.uno.XComponentContext; import com.sun.star.uno.XInterface; import com.sun.star.util.XCloneable; import complex.toolkit.awtgrid.DummyColumn; import complex.toolkit.awtgrid.TMutableGridDataModel; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.List; import java.util.Random; import org.junit.AfterClass; import org.junit.BeforeClass; import org.junit.After; import org.junit.Before; import org.junit.Test; import static org.junit.Assert.*; import org.openoffice.test.OfficeConnection; /** is a unit test for the grid control related implementations */ public class GridControl { public GridControl() { m_context = m_connection.getComponentContext(); } private static void impl_dispose( final Object... i_components ) { for ( int i=0; iXMutableGridDataModel interface */ @Test public void testMutableGridDataModel() throws Exception { impl_recreateGridModel(); TMutableGridDataModel test = new TMutableGridDataModel( m_dataModel ); test.testAddRow(); test.testAddRows(); test.testInsertRow(); test.testInsertRows(); test.testRemoveRow(); test.testRemoveAllRows(); test.testUpdateCellData(); test.testUpdateRowData(); test.testUpdateRowHeading(); test.cleanup(); // a somehwat less straight-forward test: the data model is expected to implicitly increase its column count // when you add a row which has more columns than currently known final XMutableGridDataModel dataModel = DefaultGridDataModel.create( m_context ); dataModel.addRow( 0, new Object[] { 1 } ); assertEquals( "unexpected column count after adding the most simple row", 1, dataModel.getColumnCount() ); dataModel.addRow( 1, new Object[] { 1, 2 } ); assertEquals( "implicit extension of the column count doesn't work", 2, dataModel.getColumnCount() ); } @Test public void testGridColumnModel() throws Exception { impl_recreateGridModel(); ColumnModelListener listener = new ColumnModelListener(); m_columnModel.addContainerListener( listener ); // insert default columns into the previously empty model, ensure we get the right notifications final int defaultColumnsCount = 3; m_columnModel.setDefaultColumns( defaultColumnsCount ); impl_assertColumnModelConsistency(); List< ContainerEvent > events = listener.assertExclusiveInsertionEvents(); listener.reset(); assertEquals( "wrong number of events fired by setDefaulColumns", defaultColumnsCount, events.size() ); for ( int i=0; i removalEvents = listener.getRemovalEvents(); final List< ContainerEvent > insertionEvents = listener.getInsertionEvents(); listener.reset(); // for the removal events, check the indexes assertEquals( "wrong number of columns removed (or notified) upon setting default columns", defaultColumnsCount, removalEvents.size() ); for ( int i=0; i currentSortOrder = m_dataModel.getCurrentSortOrder(); assertEquals( "invalid current sort column (column " + colIndex + ")", currentSortOrder.First.intValue(), colIndex ); assertEquals( "invalid current sort direction", currentSortOrder.Second.booleanValue(), ascending ); /*for ( int i=0; i= nextIntValue ); // ensure the data in the other columns, and the row headings, are sorted as well final Object rowHeading = m_dataModel.getRowHeading( rowIndex ); final int unsortedRowIndex = impl_assertInteger( rowHeading ); for ( int innerColIndex = 0; innerColIndex < colCount; ++innerColIndex ) { assertEquals( "sorted row " + rowIndex + ", unsorted row " + unsortedRowIndex + ", col " + innerColIndex + ": wrong data", data[unsortedRowIndex][innerColIndex], m_dataModel.getCellData( innerColIndex, rowIndex ) ); } } } } } @Test public void testView() throws Exception { final XControl control = impl_createDialogWithGridControl(); final XPropertySet gridModelProps = UnoRuntime.queryInterface( XPropertySet.class, control.getModel() ); // in the current implementation (not sure this is a good idea at all), the control (more precise: the peer) // ensures that if there are no columns in the column model, but in the data model, then the column model // will implicitly have the needed columns added. // To ensure that clients which rely on this do not break in the future, check this here. final XMutableGridDataModel dataModel = UnoRuntime.queryInterface( XMutableGridDataModel.class, gridModelProps.getPropertyValue( "GridDataModel" ) ); assertNotNull( dataModel ); assertEquals( 0, dataModel.getColumnCount() ); final XGridColumnModel columnModel = UnoRuntime.queryInterface( XGridColumnModel.class, gridModelProps.getPropertyValue( "ColumnModel" ) ); assertNotNull( columnModel ); assertEquals( 0, columnModel.getColumnCount() ); final int columnCount = 3; final int rowCount = 2; dataModel.addRow( null, new Object[] { 1, 2, 3 } ); dataModel.addRow( null, new Object[] { 6, 5, 4 } ); assertEquals( columnCount, dataModel.getColumnCount() ); assertEquals( columnCount, columnModel.getColumnCount() ); // some cursor traveling final XGridControl gridControl = UnoRuntime.queryInterface( XGridControl.class, control ); gridControl.goToCell( 0, 0 ); assertEquals( "wrong 'current column' (1)", 0, gridControl.getCurrentColumn() ); assertEquals( "wrong 'current row' (1)", 0, gridControl.getCurrentRow() ); gridControl.goToCell( columnCount - 1, rowCount - 1 ); assertEquals( "wrong 'current column' (2)", dataModel.getColumnCount() - 1, gridControl.getCurrentColumn() ); assertEquals( "wrong 'current row' (2)", dataModel.getRowCount() - 1, gridControl.getCurrentRow() ); // removing the last column, while the active cell is in this very last column, is expected to adjust // the active cell columnModel.removeColumn( columnCount - 1 ); assertEquals( "removed the last and active column, active column was not adjusted!", columnCount - 2, gridControl.getCurrentColumn() ); // same holds for rows dataModel.removeRow( rowCount - 1 ); assertEquals( "removed the last and active row, active row was not adjusted!", rowCount - 2, gridControl.getCurrentRow() ); } private XControl impl_createDialogWithGridControl() throws Exception { // create a simple dialog model/control/peer trinity final XControlModel dialogModel = createInstance( XControlModel.class, "com.sun.star.awt.UnoControlDialogModel" ); m_disposables.add( dialogModel ); final XPropertySet dialogProps = UnoRuntime.queryInterface( XPropertySet.class, dialogModel ); dialogProps.setPropertyValue( "Width", 200 ); dialogProps.setPropertyValue( "Height", 100 ); dialogProps.setPropertyValue( "Title", "Grid Control Unit Test" ); final XControl dialogControl = createInstance( XControl.class, "com.sun.star.awt.UnoControlDialog" ); m_disposables.add( dialogControl ); dialogControl.setModel( dialogModel ); dialogControl.createPeer( createInstance( XToolkit.class, "com.sun.star.awt.Toolkit" ), null ); // insert a grid control model final XMultiServiceFactory controlModelFactory = UnoRuntime.queryInterface( XMultiServiceFactory.class, dialogModel ); final XPropertySet gridModelProps = UnoRuntime.queryInterface( XPropertySet.class, controlModelFactory.createInstance( "com.sun.star.awt.grid.UnoControlGridModel" ) ); m_disposables.add( gridModelProps ); gridModelProps.setPropertyValue( "PositionX", 6 ); gridModelProps.setPropertyValue( "PositionY", 6 ); gridModelProps.setPropertyValue( "Width", 188 ); gridModelProps.setPropertyValue( "Height", 88 ); final XNameContainer modelContainer = UnoRuntime.queryInterface( XNameContainer.class, dialogModel ); modelContainer.insertByName( "grid", gridModelProps ); // check the respective control has been created final XControlContainer controlContainer = UnoRuntime.queryInterface( XControlContainer.class, dialogControl ); final XControl gridControl = controlContainer.getControl( "grid" ); assertNotNull( "no grid control created in the dialog", gridControl ); return gridControl; } private int impl_assertInteger( final Object i_object ) { assertTrue( i_object instanceof Integer ); return ((Integer)i_object).intValue(); } private void impl_assertColumnModelConsistency() throws IndexOutOfBoundsException { for ( int col = 0; col < m_columnModel.getColumnCount(); ++col ) { final XGridColumn column = m_columnModel.getColumn( col ); assertNotNull( column ); assertEquals( "column/model inconsistency: column " + col + " has a wrong index!", col, column.getIndex() ); } final XGridColumn[] allColumns = m_columnModel.getColumns(); assertEquals( "getColumns returns the wrong number of column objects", m_columnModel.getColumnCount(), allColumns.length ); for ( int col = 0; col < m_columnModel.getColumnCount(); ++col ) { assertTrue( "getColumns inconsistency", impl_areSameInterface( allColumns[col], m_columnModel.getColumn(col) ) ); } } private void impl_assertEquality( final XGridDataModel i_reference, final XGridDataModel i_compare ) throws IndexOutOfBoundsException { assertNotNull( i_reference ); assertNotNull( i_compare ); assertEquals( "data model comparison: wrong column counts", i_reference.getColumnCount(), i_compare.getColumnCount() ); assertEquals( "data model comparison: wrong row counts", i_reference.getRowCount(), i_compare.getRowCount() ); for ( int row = 0; row < i_reference.getRowCount(); ++row ) { assertEquals( "data model comparison: wrong row heading content in row " + row, i_reference.getRowHeading( row ) ); for ( int col = 0; col < i_reference.getRowCount(); ++col ) { assertEquals( "data model comparison: wrong cell content in cell (" + col + ", " + row + ")", i_reference.getCellData( col, row ) ); assertEquals( "data model comparison: wrong tooltip content in cell (" + col + ", " + row + ")", i_reference.getCellToolTip( col, row ) ); } } } private void impl_assertEquality( final XGridColumnModel i_reference, final XGridColumnModel i_compare ) throws IndexOutOfBoundsException { assertEquals( "column model comparison: wrong column counts", i_reference.getColumnCount(), i_compare.getColumnCount() ); for ( int col = 0; col < i_reference.getColumnCount(); ++col ) { final XGridColumn referenceColumn = i_reference.getColumn( col ); final XGridColumn compareColumn = i_compare.getColumn( col ); impl_assertEquality( referenceColumn, compareColumn ); } } private void impl_assertEquality( final XGridColumn i_reference, final XGridColumn i_compare ) { final Method[] methods = XGridColumn.class.getMethods(); for ( int m=0; m T createInstance( Class i_interfaceClass, final String i_serviceIndentifer ) throws Exception { return UnoRuntime.queryInterface( i_interfaceClass, createInstance( i_serviceIndentifer ) ); } private Object createInstance( final String i_serviceName ) throws Exception { Object instance = m_context.getServiceManager().createInstanceWithContext( i_serviceName, m_context ); assertNotNull( "could not create an instance of '" + i_serviceName + "'", instance ); return instance; } private static final class DisposeListener implements XEventListener { DisposeListener( final Object i_component ) { m_component = UnoRuntime.queryInterface( XComponent.class, i_component ); assertNotNull( m_component ); m_component.addEventListener( this ); } public void disposing( EventObject i_event ) { assertTrue( UnoRuntime.areSame( i_event.Source, m_component ) ); m_isDisposed = true; } final boolean isDisposed() { return m_isDisposed; } private final XComponent m_component; private boolean m_isDisposed; } private static final class ColumnModelListener implements XContainerListener { ColumnModelListener() { } public void elementInserted( ContainerEvent i_event ) { m_insertionEvents.add( i_event ); } public void elementRemoved( ContainerEvent i_event ) { m_removalEvents.add( i_event ); } public void elementReplaced( ContainerEvent i_event ) { m_replacementEvents.add( i_event ); } public void disposing( EventObject eo ) { } private List< ContainerEvent > assertExclusiveInsertionEvents() { assertFalse( m_insertionEvents.isEmpty() ); assertTrue( m_removalEvents.isEmpty() ); assertTrue( m_replacementEvents.isEmpty() ); return m_insertionEvents; } private List< ContainerEvent > assertExclusiveRemovalEvents() { assertTrue( m_insertionEvents.isEmpty() ); assertFalse( m_removalEvents.isEmpty() ); assertTrue( m_replacementEvents.isEmpty() ); return m_removalEvents; } private void reset() { m_insertionEvents = new ArrayList< ContainerEvent >(); m_removalEvents = new ArrayList< ContainerEvent >(); m_replacementEvents = new ArrayList< ContainerEvent >(); } private List< ContainerEvent > getInsertionEvents() { return m_insertionEvents; } private List< ContainerEvent > getRemovalEvents() { return m_removalEvents; } private List< ContainerEvent > m_insertionEvents = new ArrayList< ContainerEvent >(); private List< ContainerEvent > m_removalEvents = new ArrayList< ContainerEvent >(); private List< ContainerEvent > m_replacementEvents = new ArrayList< ContainerEvent >(); } private static final OfficeConnection m_connection = new OfficeConnection(); private static Random m_randomGenerator = new Random(); private final XComponentContext m_context; private XPropertySet m_gridControlModel; private XGridColumnModel m_columnModel; private XSortableMutableGridDataModel m_dataModel; private final List< Object > m_disposables = new ArrayList< Object >(); }