diff options
author | rhatcher <[email protected]> | 2012-11-29 09:11:07 -0600 |
---|---|---|
committer | rhatcher <[email protected]> | 2012-11-29 09:11:07 -0600 |
commit | 811e3791b98fea0dfa3b7d301cb532c54df8dc82 (patch) | |
tree | d9131c18c990c00c85e313d4ea6d2c50adf3b117 /src/test/com | |
parent | f25b5c973150252af5c5fbf4ca87b03e2e9aee32 (diff) |
Fix for JOGL bug 629, and added new unit tests.
The change to AWTNewtEventFactory appears to fix the original issue
for AWTCanvas instances, and the TestNewtEventModifiersAWTCanvas
appears to work ok too. However, there are still issues with
NewtCanvasAWT and NewtCanvasSWT instances. These might be problems
in the test code, but there's also a good chance there are still
issues in the NEWT event delivery infrastructure. For the time being
I recommend that only TestNewtEventModifiersAWTCanvas be included
in routine unit tests.
The tests are defined in TestNewtEventModifiers, and the remaining
test classes extend it to define how the window and associated
GL drawing surface are created.
File ModifierMappings.txt is simply informational, and shows how
the modifier bits are laid out between AWT and NEWT. This possibly
should have been a spreadsheet.
Diffstat (limited to 'src/test/com')
5 files changed, 1138 insertions, 0 deletions
diff --git a/src/test/com/jogamp/opengl/test/junit/newt/event/ModifierMappings.txt b/src/test/com/jogamp/opengl/test/junit/newt/event/ModifierMappings.txt new file mode 100644 index 000000000..1bd9102b5 --- /dev/null +++ b/src/test/com/jogamp/opengl/test/junit/newt/event/ModifierMappings.txt @@ -0,0 +1,37 @@ +Modifier AWT Constant AWT Bit AWT Ex NEWT Constant NEWT Bit +------------- ------------------------------- ------- ------ ------------------------- -------- +Shift Event.SHIFT_MASK 0 +Ctrl Event.CTRL_MASK 1 +Meta Event.META_MASK 2 +Alt Event.ALT_MASK 3 +Button1 InputEvent.BUTTON1_MASK 4 +Button2 InputEvent.BUTTON2_MASK 3 +Button3 InputEvent.BUTTON3_MASK 2 +Shift Down InputEvent.SHIFT_DOWN_MASK 6 * InputEvent.SHIFT_MASK 0 +Ctrl Down InputEvent.CTRL_DOWN_MASK 7 * InputEvent.CTRL_MASK 1 +Meta Down InputEvent.META_DOWN_MASK 8 * InputEvent.META_MASK 2 +Alt Down InputEvent.ALT_DOWN_MASK 9 * InputEvent.ALT_MASK 3 +Button1 Down InputEvent.BUTTON1_DOWN_MASK 10 * InputEvent.BUTTON1_MASK 5 +Button2 Down InputEvent.BUTTON2_DOWN_MASK 11 * InputEvent.BUTTON2_MASK 6 +Button3 Down InputEvent.BUTTON3_DOWN_MASK 12 * InputEvent.BUTTON3_MASK 7 +AltGraph Down InputEvent.ALT_GRAPH_DOWN_MASK 13 * InputEvent.ALT_GRAPH_MASK 4 +Button4 Down -- 14 * InputEvent.BUTTON4_MASK 8 +Button5 Down -- 15 * InputEvent.BUTTON5_MASK 9 +Button6 Down -- 16 * InputEvent.BUTTON6_MASK 10 +Button7 Down -- 17 * InputEvent.BUTTON7_MASK 11 +Button8 Down -- 18 * InputEvent.BUTTON8_MASK 12 +Button9 Down -- 19 * InputEvent.BUTTON9_MASK 13 +Button10 Down -- 20 * +Button11 Down -- 21 * +Button12 Down -- 22 * +Button13 Down -- 23 * +Button14 Down -- 24 * +Button15 Down -- 25 * +Button16 Down -- 26 * InputEvent.BUTTONLAST_MASK 20 +Button17 Down -- 27 * +Button18 Down -- 28 * +Button19 Down -- 29 * +Button20 Down -- 30 * +Autorepeat -- - InputEvent.AUTOREPEAT_MASK 29 +Confined -- - InputEvent.CONFINED_MASK 30 +Invisible -- - InputEvent.INVISIBLE_MASK 31 diff --git a/src/test/com/jogamp/opengl/test/junit/newt/event/TestNewtEventModifiers.java b/src/test/com/jogamp/opengl/test/junit/newt/event/TestNewtEventModifiers.java new file mode 100644 index 000000000..36185f8ce --- /dev/null +++ b/src/test/com/jogamp/opengl/test/junit/newt/event/TestNewtEventModifiers.java @@ -0,0 +1,738 @@ +/** + * Copyright 2010 JogAmp Community. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ + +package com.jogamp.opengl.test.junit.newt.event ; + +import java.io.PrintStream ; +import java.util.ArrayList ; + +import javax.media.opengl.GL ; +import javax.media.opengl.GL2 ; +import javax.media.opengl.GLAutoDrawable ; +import javax.media.opengl.GLEventListener ; +import javax.media.opengl.GLProfile ; + +import org.junit.After ; +import org.junit.AfterClass ; +import org.junit.Assert ; +import org.junit.Before ; +import org.junit.BeforeClass ; +import org.junit.Test ; + +import com.jogamp.opengl.test.junit.util.UITestCase ; + +/** + * Test whether or not event modifiers are preserved by NEWT. This + * class defines most of the tests, but leaves the type of window + * and canvas up to subclasses. + */ + +public abstract class TestNewtEventModifiers extends UITestCase { + + static + { + GLProfile.initSingleton() ; + } + + // A class to draw something on a canvas. This is only + // here so we can make sure the GL drawing area has + // filled the window. + + public static class BigGreenXGLEventListener implements GLEventListener + { + public void init( GLAutoDrawable drawable ) + { + GL2 gl = drawable.getGL().getGL2() ; + + gl.glClearColor( 0.0f, 0.0f, 0.0f, 1.0f ) ; + + gl.glEnable( GL.GL_LINE_SMOOTH ) ; + gl.glEnable( GL.GL_BLEND ) ; + gl.glBlendFunc( GL.GL_SRC_ALPHA, GL.GL_ONE_MINUS_SRC_ALPHA ) ; + } + + public void reshape( GLAutoDrawable drawable, int x, int y, int width, int height ) + { + GL2 gl = drawable.getGL().getGL2() ; + + gl.glViewport( 0, 0, width, height ) ; + + gl.glMatrixMode( GL2.GL_PROJECTION ) ; + gl.glLoadIdentity() ; + gl.glOrtho( -1.0, 1.0, -1.0, 1.0, -1.0, 1.0 ) ; + + gl.glMatrixMode( GL2.GL_MODELVIEW ) ; + gl.glLoadIdentity() ; + } + + public void display( GLAutoDrawable drawable ) + { + GL2 gl = drawable.getGL().getGL2() ; + + gl.glClear( GL2.GL_COLOR_BUFFER_BIT ) ; + + gl.glColor4f( 0.0f, 1.0f, 0.0f, 1.0f ) ; + + gl.glBegin( GL.GL_LINES ) ; + { + gl.glVertex2f( -1.0f, 1.0f ) ; + gl.glVertex2f( 1.0f, -1.0f ) ; + + gl.glVertex2f( -1.0f, -1.0f ) ; + gl.glVertex2f( 1.0f, 1.0f ) ; + } + gl.glEnd() ; + } + + public void dispose( GLAutoDrawable drawable ) + { + } + } + + //////////////////////////////////////////////////////////////////////////// + + private static class TestMouseListener implements com.jogamp.newt.event.MouseListener + { + private static final String NO_EVENT_DELIVERY = "no event delivery" ; + + private boolean _modifierCheckEnabled ; + private int _expectedModifiers ; + private ArrayList<String> _failures = new ArrayList<String>() ; + + public void setModifierCheckEnabled( boolean value ) { + _modifierCheckEnabled = value ; + } + + public boolean modifierCheckEnabled() { + return _modifierCheckEnabled ; + } + + /** + * Sets the modifiers the listener should expect, and clears + * out any existing accumulated failures. Normally this kind + * of double duty in a setter might be considered evil, but + * in this test code it's probably ok. + */ + + public void setExpectedModifiers( int value ) { + _expectedModifiers = value ; + + // Assume we will have a failure due to no event delivery. + // If an event is delivered and it's good this assumed + // failure will get cleared out. + + _failures.clear() ; + _failures.add( NO_EVENT_DELIVERY ) ; + } + + private void _checkModifiers( com.jogamp.newt.event.MouseEvent event ) { + + if( _debug ) { + _debugPrintStream.print( " received NEWT " ) ; + _debugPrintStream.print( com.jogamp.newt.event.MouseEvent.getEventTypeString( event.getEventType() ) ) ; + } + + if( _modifierCheckEnabled ) { + + if( _debug ) { + _debugPrintStream.println( ", checking modifiers..." ) ; + _debugPrintStream.println( " expected NEWT Modifiers:" ) ; + _printModifiers( _expectedModifiers ) ; + _debugPrintStream.println( " current NEWT Modifiers:" ) ; + _printModifiers( event.getModifiers() ) ; + } + + _checkModifierMask( event, com.jogamp.newt.event.InputEvent.SHIFT_MASK ) ; + _checkModifierMask( event, com.jogamp.newt.event.InputEvent.CTRL_MASK ) ; + _checkModifierMask( event, com.jogamp.newt.event.InputEvent.META_MASK ) ; + _checkModifierMask( event, com.jogamp.newt.event.InputEvent.ALT_MASK ) ; + _checkModifierMask( event, com.jogamp.newt.event.InputEvent.ALT_GRAPH_MASK ) ; + + for( int n = 0 ; n < _numButtonsToTest ; ++n ) { + _checkModifierMask( event, com.jogamp.newt.event.InputEvent.getButtonMask( n + 1 ) ) ; + } + } else { + if( _debug ) { + _debugPrintStream.println( ", modifier check disabled" ) ; + } + } + } + + private void _checkModifierMask( com.jogamp.newt.event.MouseEvent event, int mask ) { + + // If the "no event delivery" failure is still in the list then + // get rid of it since that obviously isn't true anymore. We + // want to do this whether or not there's an issue with the + // modifiers. + + if( _failures.size() == 1 && _failures.get(0).equals( NO_EVENT_DELIVERY ) ) { + _failures.clear() ; + } + + if( ( event.getModifiers() & mask ) != ( _expectedModifiers & mask ) ) { + _failures.add( com.jogamp.newt.event.MouseEvent.getEventTypeString( event.getEventType() ) ) ; + } + } + + private void _printModifiers( int modifiers ) { + if( ( modifiers & com.jogamp.newt.event.InputEvent.SHIFT_MASK ) != 0 ) { _debugPrintStream.println( " SHIFT" ) ; } + if( ( modifiers & com.jogamp.newt.event.InputEvent.CTRL_MASK ) != 0 ) { _debugPrintStream.println( " CTRL" ) ; } + if( ( modifiers & com.jogamp.newt.event.InputEvent.META_MASK ) != 0 ) { _debugPrintStream.println( " META" ) ; } + if( ( modifiers & com.jogamp.newt.event.InputEvent.ALT_MASK ) != 0 ) { _debugPrintStream.println( " ALT" ) ; } + if( ( modifiers & com.jogamp.newt.event.InputEvent.ALT_GRAPH_MASK ) != 0 ) { _debugPrintStream.println( " ALT_GRAPH" ) ; } + if( ( modifiers & com.jogamp.newt.event.InputEvent.BUTTON1_MASK ) != 0 ) { _debugPrintStream.println( " BUTTON1" ) ; } + if( ( modifiers & com.jogamp.newt.event.InputEvent.BUTTON2_MASK ) != 0 ) { _debugPrintStream.println( " BUTTON2" ) ; } + if( ( modifiers & com.jogamp.newt.event.InputEvent.BUTTON3_MASK ) != 0 ) { _debugPrintStream.println( " BUTTON3" ) ; } + if( ( modifiers & com.jogamp.newt.event.InputEvent.BUTTON4_MASK ) != 0 ) { _debugPrintStream.println( " BUTTON4" ) ; } + if( ( modifiers & com.jogamp.newt.event.InputEvent.BUTTON5_MASK ) != 0 ) { _debugPrintStream.println( " BUTTON5" ) ; } + if( ( modifiers & com.jogamp.newt.event.InputEvent.BUTTON6_MASK ) != 0 ) { _debugPrintStream.println( " BUTTON6" ) ; } + if( ( modifiers & com.jogamp.newt.event.InputEvent.BUTTON7_MASK ) != 0 ) { _debugPrintStream.println( " BUTTON7" ) ; } + if( ( modifiers & com.jogamp.newt.event.InputEvent.BUTTON8_MASK ) != 0 ) { _debugPrintStream.println( " BUTTON8" ) ; } + if( ( modifiers & com.jogamp.newt.event.InputEvent.BUTTON9_MASK ) != 0 ) { _debugPrintStream.println( " BUTTON9" ) ; } + } + + public ArrayList<String> getFailures() { + return _failures ; + } + + public void mouseClicked( com.jogamp.newt.event.MouseEvent event ) { + _checkModifiers( event ) ; + } + + public void mousePressed( com.jogamp.newt.event.MouseEvent event ) { + _checkModifiers( event ) ; + } + + public void mouseReleased( com.jogamp.newt.event.MouseEvent event ) { + _checkModifiers( event ) ; + } + + public void mouseEntered( com.jogamp.newt.event.MouseEvent event ) { + _checkModifiers( event ) ; + } + + public void mouseExited( com.jogamp.newt.event.MouseEvent event ) { + _checkModifiers( event ) ; + } + + public void mouseDragged( com.jogamp.newt.event.MouseEvent event ) { + _checkModifiers( event ) ; + } + + public void mouseMoved( com.jogamp.newt.event.MouseEvent event ) { + _checkModifiers( event ) ; + } + + public void mouseWheelMoved( com.jogamp.newt.event.MouseEvent event ) { + _checkModifiers( event ) ; + } + } + + //////////////////////////////////////////////////////////////////////////// + + protected static final int TEST_FRAME_X = 100 ; + protected static final int TEST_FRAME_Y = 100 ; + + protected static final int TEST_FRAME_WIDTH = 400 ; + protected static final int TEST_FRAME_HEIGHT = 400 ; + + private static final int INITIAL_MOUSE_X = TEST_FRAME_X + ( TEST_FRAME_WIDTH / 2 ) ; + private static final int INITIAL_MOUSE_Y = TEST_FRAME_Y + ( TEST_FRAME_HEIGHT / 2 ) ; + + private static final int MS_ROBOT_SHORT_AUTO_DELAY = 50 ; + private static final int MS_ROBOT_LONG_AUTO_DELAY = 500 ; + + private static boolean _debug = true ; + private static PrintStream _debugPrintStream = System.err ; + + //////////////////////////////////////////////////////////////////////////// + + private static int _numButtonsToTest ; + private static int _awtButtonMasks[] ; + private static int _newtButtonMasks[] ; + + private static java.awt.Robot _robot ; + + protected static TestMouseListener _testMouseListener ; + + //////////////////////////////////////////////////////////////////////////// + + @BeforeClass + public static void beforeClass() throws Exception { + + // Who know how many buttons the AWT will say exist on given platform. + // We'll test the smaller of what NEWT supports and what the + // AWT says is available. + + if( java.awt.Toolkit.getDefaultToolkit().areExtraMouseButtonsEnabled() ) { + _numButtonsToTest = java.awt.MouseInfo.getNumberOfButtons() ; + } else { + _numButtonsToTest = 3 ; + } + + // Then again, maybe not: + + // FIXME? - for reasons I'm not quite sure of the AWT MouseEvent + // constructor does some strange things for buttons other than + // 1, 2, and 3. Furthermore, while developing this test it + // appeared that events sent by the robot for buttons 9 and + // up weren't even delivered to the listeners. + // + // So... for now we're only going to test 3 buttons since + // that's the common case. + + _numButtonsToTest = 3 ; + + { + if( _numButtonsToTest > com.jogamp.newt.event.MouseEvent.BUTTON_NUMBER ) { + _numButtonsToTest = com.jogamp.newt.event.MouseEvent.BUTTON_NUMBER ; + } + + // These two arrays are assumed to be peers, i.e. are the same + // size, and a given index references the same button in + // either array. + + _awtButtonMasks = new int[_numButtonsToTest] ; + + for( int n = 0 ; n < _awtButtonMasks.length ; ++n ) { + _awtButtonMasks[n] = java.awt.event.InputEvent.getMaskForButton( n + 1 ) ; + } + + _newtButtonMasks = new int[_numButtonsToTest] ; + + for( int n = 0 ; n < _newtButtonMasks.length ; ++n ) { + _newtButtonMasks[n] = com.jogamp.newt.event.InputEvent.getButtonMask( n + 1 ) ; + } + } + + _robot = new java.awt.Robot() ; + _robot.setAutoWaitForIdle( true ) ; + _robot.setAutoDelay( MS_ROBOT_LONG_AUTO_DELAY ) ; + + _testMouseListener = new TestMouseListener() ; + } + + //////////////////////////////////////////////////////////////////////////// + + @Before + public void before() throws Exception { + + _testMouseListener.setModifierCheckEnabled( false ) ; + _robot.setAutoDelay( MS_ROBOT_SHORT_AUTO_DELAY ) ; + + // Make sure all the buttons and modifier keys are released. + + _releaseModifiers() ; + _escape() ; + + // Move the pointer into the window and click once to + // ensure that the test window has focus. + + if( _debug ) { + _debugPrintStream.println( ">>>> Clicking in the window to get focus." ) ; + } + + _robot.mouseMove( INITIAL_MOUSE_X, INITIAL_MOUSE_Y ) ; + _robot.mousePress( java.awt.event.InputEvent.BUTTON1_DOWN_MASK ) ; + _robot.mouseRelease( java.awt.event.InputEvent.BUTTON1_DOWN_MASK ) ; + + _testMouseListener.setModifierCheckEnabled( true ) ; + _robot.setAutoDelay( MS_ROBOT_LONG_AUTO_DELAY ) ; + + if( _debug ) { + _debugPrintStream.println( ">>>> About to start testing." ) ; + } + } + + //////////////////////////////////////////////////////////////////////////// + + // The approach on all these tests is to tell the test mouse listener what + // modifiers we think it should receive. Then when the events are delivered + // it compares what we told it to expect with what actually showed up and + // complains if there are differences. + // + // As things stand currently the tests below generally work for AWTCanvas + // and fail for everything else. This may point to a flaw in the test + // code, or a flaw in the NEWT stuff; not sure yet. One exception is the + // tests involving ALT and META, which on at least X11 cause the desktop + // to do undesirable stuff while the tests are in progress. So... these + // tests have been commented out for now and probably should be left + // that way. + // + // Due to the fact that a majority of these fail currently for + // everything but AWTCanvas for the time being we probably shouldn't + // run the tests for NewtCanvasAWT and NewtCanvasSWT until we can + // pay more attention to the NEWT event modifier stuff. + + @Test + public void testSingleButtonPressAndRelease() throws Exception { + _doSingleButtonPressAndRelease( 0, 0 ) ; + } + + @Test + public void testSingleButtonPressAndReleaseWithShift() throws Exception { + _doSingleButtonPressAndRelease( java.awt.event.KeyEvent.VK_SHIFT, java.awt.event.InputEvent.SHIFT_DOWN_MASK ) ; + } + + @Test + public void testSingleButtonPressAndReleaseWithCtrl() throws Exception { + _doSingleButtonPressAndRelease( java.awt.event.KeyEvent.VK_CONTROL, java.awt.event.InputEvent.CTRL_DOWN_MASK ) ; + } + + // The META and ALT tests get too tied up with functions of the window system on X11, + // so it's probably best to leave them commented out. + + //@Test + //public void testSingleButtonPressAndReleaseWithMeta() throws Exception { + // _doSingleButtonPressAndRelease( java.awt.event.KeyEvent.VK_META, java.awt.event.InputEvent.META_DOWN_MASK ) ; + //} + + //@Test + //public void testSingleButtonPressAndReleaseWithAlt() throws Exception { + // _doSingleButtonPressAndRelease( java.awt.event.KeyEvent.VK_ALT, java.awt.event.InputEvent.ALT_DOWN_MASK ) ; + //} + + // FIXME - not sure yet what's up with ALT_GRAPH. It appears that this + // modifier didn't make it through, so I had to disable this test else + // it would always fail. + // + // My US keyboard doesn't have an AltGr key, so maybe X is smart + // enough to not let this modifier slip through (?). + + //@Test + //public void testSingleButtonPressAndReleaseWithAltGraph() throws Exception { + // _doSingleButtonPressAndRelease( java.awt.event.KeyEvent.VK_ALT_GRAPH, java.awt.event.InputEvent.ALT_GRAPH_DOWN_MASK ) ; + //} + + //////////////////////////////////////////////////////////////////////////// + + @Test + public void testHoldOneButtonAndPressAnother() throws Exception { + _doHoldOneButtonAndPressAnother( 0, 0 ) ; + } + + @Test + public void testPressAllButtonsInSequence() throws Exception { + _doPressAllButtonsInSequence( 0, 0 ) ; + } + + @Test + public void testSingleButtonClickAndDrag() throws Exception { + _doSingleButtonClickAndDrag( 0, 0 ) ; + } + + //////////////////////////////////////////////////////////////////////////// + + private void _doSingleButtonPressAndRelease( final int keyCode, final int keyModifierMask ) throws Exception { + + if( _debug ) { _debugPrintStream.println( "\n>>>> _doSingleButtonPressAndRelease" ) ; } + + _doKeyPress( keyCode ) ; + + for (int n = 0 ; n < _numButtonsToTest ; ++n) { + + int awtButtonMask = _awtButtonMasks[n] ; + + if( _debug ) { _debugPrintStream.println( "\n pressing button " + ( n + 1 ) ) ; } + _testMouseListener.setExpectedModifiers( _getNewtModifiersForAwtExtendedModifiers( keyModifierMask | awtButtonMask ) ) ; + _robot.mousePress( awtButtonMask ) ; + _checkFailures() ; + + if( _debug ) { _debugPrintStream.println( "\n releasing button " + ( n + 1 ) ) ; } + _testMouseListener.setExpectedModifiers( _getNewtModifiersForAwtExtendedModifiers( keyModifierMask ) ) ; + _robot.mouseRelease( awtButtonMask ) ; + _checkFailures() ; + } + + _doKeyRelease( keyCode ) ; + } + + //////////////////////////////////////////////////////////////////////////// + + private void _doHoldOneButtonAndPressAnother( final int keyCode, final int keyModifierMask ) throws Exception { + + if( _debug ) { _debugPrintStream.println( "\n>>>> _doHoldOneButtonAndPressAnother" ) ; } + + _doKeyPress( keyCode ) ; + + for (int n = 0 ; n < _numButtonsToTest ; ++n) { + + int awtButtonMask = _awtButtonMasks[n] ; + + if( _debug ) { _debugPrintStream.println( "\n pressing button " + ( n + 1 ) ) ; } + _testMouseListener.setExpectedModifiers( _getNewtModifiersForAwtExtendedModifiers( keyModifierMask | awtButtonMask ) ) ; + _robot.mousePress( awtButtonMask ) ; + _checkFailures() ; + + for (int m = 0 ; m < _numButtonsToTest ; ++m) { + + if( n != m ) { + + if( _debug ) { _debugPrintStream.println( "\n pressing additional button " + ( m + 1 ) ) ; } + _testMouseListener.setExpectedModifiers( _getNewtModifiersForAwtExtendedModifiers( keyModifierMask | awtButtonMask | _awtButtonMasks[m] ) ) ; + _robot.mousePress( _awtButtonMasks[m] ) ; + _checkFailures() ; + + if( _debug ) { _debugPrintStream.println( "\n releasing additional button " + ( m + 1 ) ) ; } + _testMouseListener.setExpectedModifiers( _getNewtModifiersForAwtExtendedModifiers( keyModifierMask | awtButtonMask ) ) ; + _robot.mouseRelease( _awtButtonMasks[m] ) ; + _checkFailures() ; + } + } + + if( _debug ) { _debugPrintStream.println( "\n releasing button " + ( n + 1 ) ) ; } + _testMouseListener.setExpectedModifiers( _getNewtModifiersForAwtExtendedModifiers( keyModifierMask ) ) ; + _robot.mouseRelease( awtButtonMask ) ; + _checkFailures() ; + } + + _doKeyRelease( keyCode ) ; + } + + //////////////////////////////////////////////////////////////////////////// + + private void _doPressAllButtonsInSequence( final int keyCode, final int keyModifierMask ) throws Exception { + + if( _debug ) { _debugPrintStream.println( "\n>>>> _doPressAllButtonsInSequence" ) ; } + + _doKeyPress( keyCode ) ; + + { + int cumulativeAwtModifiers = 0 ; + + for (int n = 0 ; n < _numButtonsToTest ; ++n) { + + cumulativeAwtModifiers |= _awtButtonMasks[n] ; + + if( _debug ) { _debugPrintStream.println( "\n pressing button " + ( n + 1 ) ) ; } + _testMouseListener.setExpectedModifiers( _getNewtModifiersForAwtExtendedModifiers( keyModifierMask | cumulativeAwtModifiers ) ) ; + _robot.mousePress( _awtButtonMasks[n] ) ; + _checkFailures() ; + } + + for (int n = _numButtonsToTest - 1 ; n >= 0 ; --n) { + + cumulativeAwtModifiers &= ~_awtButtonMasks[n] ; + + if( _debug ) { _debugPrintStream.println( "\n releasing button " + ( n + 1 ) ) ; } + _testMouseListener.setExpectedModifiers( _getNewtModifiersForAwtExtendedModifiers( keyModifierMask | cumulativeAwtModifiers ) ) ; + _robot.mouseRelease( _awtButtonMasks[n] ) ; + _checkFailures() ; + } + } + + _doKeyRelease( keyCode ) ; + } + + //////////////////////////////////////////////////////////////////////////// + + private void _doSingleButtonClickAndDrag( final int keyCode, final int keyModifierMask ) throws Exception { + + if( _debug ) { _debugPrintStream.println( "\n>>>> _doSingleButtonClickAndDrag" ) ; } + + _doKeyPress( keyCode ) ; + + _testMouseListener.setModifierCheckEnabled( true ) ; + + for (int n = 0 ; n < _numButtonsToTest ; ++n) { + + int awtButtonMask = _awtButtonMasks[n] ; + + if( _debug ) { _debugPrintStream.println( "\n pressing button " + ( n + 1 ) ) ; } + _testMouseListener.setExpectedModifiers( _getNewtModifiersForAwtExtendedModifiers( keyModifierMask | awtButtonMask ) ) ; + _robot.mousePress( awtButtonMask ) ; + _checkFailures() ; + + // To get a drag we only need to move one pixel. + if( _debug ) { _debugPrintStream.println( "\n moving mouse" ) ; } + _robot.mouseMove( INITIAL_MOUSE_X + 1, INITIAL_MOUSE_Y + 1 ) ; + _checkFailures() ; + + if( _debug ) { _debugPrintStream.println( "\n releasing button " + ( n + 1 ) ) ; } + _testMouseListener.setExpectedModifiers( _getNewtModifiersForAwtExtendedModifiers( keyModifierMask ) ) ; + _robot.mouseRelease( awtButtonMask ) ; + _checkFailures() ; + + _testMouseListener.setModifierCheckEnabled( false ) ; + _robot.mouseMove( INITIAL_MOUSE_X, INITIAL_MOUSE_Y ) ; + _testMouseListener.setModifierCheckEnabled( true ) ; + } + + _doKeyRelease( keyCode ) ; + } + + //////////////////////////////////////////////////////////////////////////// + + private void _doKeyPress( int keyCode ) { + if( keyCode != 0 ) { + boolean modifierCheckEnabled = _testMouseListener.modifierCheckEnabled() ; + _testMouseListener.setModifierCheckEnabled( false ) ; + _robot.keyPress( keyCode ) ; + _testMouseListener.setModifierCheckEnabled( modifierCheckEnabled ) ; + } + } + + //////////////////////////////////////////////////////////////////////////// + + private void _doKeyRelease( int keyCode ) { + if( keyCode != 0 ) { + boolean modifierCheckEnabled = _testMouseListener.modifierCheckEnabled() ; + _testMouseListener.setModifierCheckEnabled( false ) ; + _robot.keyRelease( keyCode ) ; + _testMouseListener.setModifierCheckEnabled( modifierCheckEnabled ) ; + } + } + + //////////////////////////////////////////////////////////////////////////// + + private void _checkFailures() { + + ArrayList<String> failures = _testMouseListener.getFailures() ; + + if( _debug ) { + int numFailures = failures.size() ; + if( numFailures == 0 ) { + _debugPrintStream.println( " PASSED" ) ; + } else { + _debugPrintStream.println( " FAILED" ) ; + for( int n = 0 ; n < numFailures ; ++n ) { + _debugPrintStream.print( " " ) ; + _debugPrintStream.println( failures.get(n) ) ; + } + } + } + + Assert.assertTrue( failures.size() == 0 ) ; + } + + //////////////////////////////////////////////////////////////////////////// + + @After + public void after() throws Exception { + + _testMouseListener.setModifierCheckEnabled( false ) ; + + Thread.sleep( 500 ) ; + } + + //////////////////////////////////////////////////////////////////////////// + + @AfterClass + public static void afterClass() throws Exception { + + // Make sure all modifiers are released, otherwise the user's + // desktop can get locked up (ask me how I know this). + + _releaseModifiers() ; + _escape() ; + } + + //////////////////////////////////////////////////////////////////////////// + + private static void _releaseModifiers() { + + if (_robot != null) { + + _robot.setAutoDelay( MS_ROBOT_SHORT_AUTO_DELAY ) ; + + boolean modifierCheckEnabled = _testMouseListener.modifierCheckEnabled() ; + _testMouseListener.setModifierCheckEnabled( false ) ; + + { + _robot.keyRelease( java.awt.event.KeyEvent.VK_SHIFT ) ; + _robot.keyRelease( java.awt.event.KeyEvent.VK_CONTROL ) ; + _robot.keyRelease( java.awt.event.KeyEvent.VK_META ) ; + _robot.keyRelease( java.awt.event.KeyEvent.VK_ALT ) ; + _robot.keyRelease( java.awt.event.KeyEvent.VK_ALT_GRAPH ) ; + + for (int n = 0 ; n < _awtButtonMasks.length ; ++n) { + _robot.mouseRelease( _awtButtonMasks[n] ) ; + } + } + + _testMouseListener.setModifierCheckEnabled( modifierCheckEnabled ) ; + + _robot.setAutoDelay( MS_ROBOT_LONG_AUTO_DELAY ) ; + } + } + + private static void _escape() { + if (_robot != null) { + _robot.keyPress( java.awt.event.KeyEvent.VK_ESCAPE ) ; + _robot.keyRelease( java.awt.event.KeyEvent.VK_ESCAPE ) ; + } + } + + //////////////////////////////////////////////////////////////////////////// + + /** + * Brute force method to return the NEWT event modifiers equivalent + * to the specified AWT event extended modifiers. + * + * @param awtExtendedModifiers + * The AWT extended modifiers. + * + * @return + * The equivalent NEWT modifiers. + */ + + private int _getNewtModifiersForAwtExtendedModifiers( int awtExtendedModifiers ) { + + int mask = 0 ; + + if( ( awtExtendedModifiers & java.awt.event.InputEvent.SHIFT_DOWN_MASK ) != 0 ) { + mask |= com.jogamp.newt.event.InputEvent.SHIFT_MASK ; + } + + if( ( awtExtendedModifiers & java.awt.event.InputEvent.CTRL_DOWN_MASK ) != 0 ) { + mask |= com.jogamp.newt.event.InputEvent.CTRL_MASK ; + } + + if( ( awtExtendedModifiers & java.awt.event.InputEvent.META_DOWN_MASK ) != 0 ) { + mask |= com.jogamp.newt.event.InputEvent.META_MASK ; + } + + if( ( awtExtendedModifiers & java.awt.event.InputEvent.ALT_DOWN_MASK ) != 0 ) { + mask |= com.jogamp.newt.event.InputEvent.ALT_MASK ; + } + + if( ( awtExtendedModifiers & java.awt.event.InputEvent.ALT_GRAPH_DOWN_MASK ) != 0 ) { + mask |= com.jogamp.newt.event.InputEvent.ALT_GRAPH_MASK ; + } + + for (int n = 0 ; n < _numButtonsToTest ; ++n) { + if ((awtExtendedModifiers & java.awt.event.InputEvent.getMaskForButton(n+1)) != 0) { + mask |= com.jogamp.newt.event.InputEvent.getButtonMask(n+1) ; + } + } + + return mask ; + } + + //////////////////////////////////////////////////////////////////////////// +} diff --git a/src/test/com/jogamp/opengl/test/junit/newt/event/TestNewtEventModifiersAWTCanvas.java b/src/test/com/jogamp/opengl/test/junit/newt/event/TestNewtEventModifiersAWTCanvas.java new file mode 100644 index 000000000..3aa016d56 --- /dev/null +++ b/src/test/com/jogamp/opengl/test/junit/newt/event/TestNewtEventModifiersAWTCanvas.java @@ -0,0 +1,96 @@ +/** + * Copyright 2010 JogAmp Community. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ + +package com.jogamp.opengl.test.junit.newt.event; + +import javax.media.opengl.awt.GLCanvas ; + +import javax.swing.JFrame ; +import javax.swing.SwingUtilities ; +import javax.swing.WindowConstants ; + +import org.junit.AfterClass ; +import org.junit.BeforeClass ; + +import com.jogamp.newt.event.awt.AWTMouseAdapter ; + +/** + * Test whether or not event modifiers are preserved by NEWT when + * the source is an AWT canvas. + */ + +public class TestNewtEventModifiersAWTCanvas extends TestNewtEventModifiers { + + private static JFrame _testFrame ; + + //////////////////////////////////////////////////////////////////////////// + + @BeforeClass + public static void beforeClass() throws Exception { + + TestNewtEventModifiers.beforeClass() ; + + final GLCanvas canvas = new GLCanvas() ; + canvas.addGLEventListener( new BigGreenXGLEventListener() ) ; + new AWTMouseAdapter( _testMouseListener ).addTo( (java.awt.Component)canvas ) ; + + _testFrame = new JFrame( "Event Modifier Test AWTCanvas" ) ; + _testFrame.setDefaultCloseOperation( WindowConstants.EXIT_ON_CLOSE ) ; + _testFrame.getContentPane().add( canvas ) ; + + SwingUtilities.invokeAndWait(new Runnable() { + public void run() { + _testFrame.setBounds( TEST_FRAME_X, TEST_FRAME_Y, TEST_FRAME_WIDTH, TEST_FRAME_HEIGHT ) ; + _testFrame.setVisible( true ) ; + } + }) ; + } + + //////////////////////////////////////////////////////////////////////////// + + @AfterClass + public static void afterClass() throws Exception { + + TestNewtEventModifiers.afterClass() ; + + SwingUtilities.invokeAndWait(new Runnable() { + public void run() { + _testFrame.dispose() ; + } + }) ; + } + + //////////////////////////////////////////////////////////////////////////// + + public static void main(String args[]) throws Exception { + String testName = TestNewtEventModifiersAWTCanvas.class.getName() ; + org.junit.runner.JUnitCore.main( testName ) ; + } + + //////////////////////////////////////////////////////////////////////////// +} diff --git a/src/test/com/jogamp/opengl/test/junit/newt/event/TestNewtEventModifiersNewtCanvasAWT.java b/src/test/com/jogamp/opengl/test/junit/newt/event/TestNewtEventModifiersNewtCanvasAWT.java new file mode 100644 index 000000000..a2c1d3364 --- /dev/null +++ b/src/test/com/jogamp/opengl/test/junit/newt/event/TestNewtEventModifiersNewtCanvasAWT.java @@ -0,0 +1,111 @@ +/** + * Copyright 2010 JogAmp Community. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ + +package com.jogamp.opengl.test.junit.newt.event ; + +import java.awt.BorderLayout ; + +import javax.media.opengl.GLCapabilities ; +import javax.media.opengl.GLProfile ; + +import javax.swing.JFrame ; +import javax.swing.SwingUtilities ; +import javax.swing.WindowConstants ; + +import org.junit.AfterClass ; +import org.junit.BeforeClass ; + +import com.jogamp.newt.awt.NewtCanvasAWT ; +import com.jogamp.newt.opengl.GLWindow ; + +/** + * Test whether or not event modifiers are preserved by NEWT when + * the canvas is a NewtCanvasAWT. + */ + +public class TestNewtEventModifiersNewtCanvasAWT extends TestNewtEventModifiers { + + private static JFrame _testFrame ; + private static GLWindow _glWindow ; + + //////////////////////////////////////////////////////////////////////////// + + @BeforeClass + public static void beforeClass() throws Exception { + + TestNewtEventModifiers.beforeClass() ; + + SwingUtilities.invokeAndWait( new Runnable() { + public void run() { + + _testFrame = new JFrame( "Event Modifier Test NewtCanvasAWT" ) ; + _testFrame.setDefaultCloseOperation( WindowConstants.EXIT_ON_CLOSE ) ; + + { + GLCapabilities caps = new GLCapabilities( GLProfile.getGL2ES2() ) ; + _glWindow = GLWindow.create( caps ) ; + + NewtCanvasAWT canvas = new NewtCanvasAWT( _glWindow ) ; + _testFrame.getContentPane().add( canvas, BorderLayout.CENTER ) ; + + _glWindow.addGLEventListener( new BigGreenXGLEventListener() ) ; + } + + _testFrame.setBounds( TEST_FRAME_X, TEST_FRAME_Y, TEST_FRAME_WIDTH, TEST_FRAME_HEIGHT ) ; + _testFrame.setVisible( true ) ; + } + } ) ; + + _glWindow.addMouseListener( _testMouseListener ) ; + } + + //////////////////////////////////////////////////////////////////////////// + + @AfterClass + public static void afterClass() throws Exception { + + TestNewtEventModifiers.afterClass() ; + + SwingUtilities.invokeAndWait( new Runnable() { + public void run() { + _testFrame.dispose() ; + } + } ) ; + + _glWindow.destroy() ; + } + + //////////////////////////////////////////////////////////////////////////// + + public static void main(String args[]) throws Exception { + String testName = TestNewtEventModifiersNewtCanvasAWT.class.getName() ; + org.junit.runner.JUnitCore.main( testName ) ; + } + + //////////////////////////////////////////////////////////////////////////// +} diff --git a/src/test/com/jogamp/opengl/test/junit/newt/event/TestNewtEventModifiersNewtCanvasSWT.java b/src/test/com/jogamp/opengl/test/junit/newt/event/TestNewtEventModifiersNewtCanvasSWT.java new file mode 100644 index 000000000..e6139bb37 --- /dev/null +++ b/src/test/com/jogamp/opengl/test/junit/newt/event/TestNewtEventModifiersNewtCanvasSWT.java @@ -0,0 +1,156 @@ +/** + * Copyright 2010 JogAmp Community. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ + +package com.jogamp.opengl.test.junit.newt.event; + +import org.eclipse.swt.SWT ; +import org.eclipse.swt.layout.FillLayout ; +import org.eclipse.swt.widgets.Display ; +import org.eclipse.swt.widgets.Shell ; + +import javax.media.opengl.GLCapabilities ; +import javax.media.opengl.GLProfile ; + +import org.junit.AfterClass ; +import org.junit.BeforeClass ; + +import com.jogamp.newt.opengl.GLWindow ; +import com.jogamp.newt.swt.NewtCanvasSWT ; + +/** + * Test whether or not event modifiers preserved by NEWT when + * the canvas is a NewtCanvasSWT. + */ + +public class TestNewtEventModifiersNewtCanvasSWT extends TestNewtEventModifiers { + + private static Shell _testShell ; + private static GLWindow _glWindow ; + private static DisplayThread _displayThread ; + + //////////////////////////////////////////////////////////////////////////// + + private static class DisplayThread extends Thread + { + private Display _display ; + + public DisplayThread() + { + super( "SWT Display Thread" ) ; + } + + public Display getDisplay() { + return _display ; + } + + public void run() { + + _display = new Display() ; + + while( isInterrupted() == false ) { + if( !_display.readAndDispatch() ) { + _display.sleep() ; + } + } + + _display.dispose() ; + } + } + + //////////////////////////////////////////////////////////////////////////// + + @BeforeClass + public static void beforeClass() throws Exception { + + TestNewtEventModifiers.beforeClass() ; + + _displayThread = new DisplayThread() ; + _displayThread.start() ; + + // Wait for the display thread's runnable to get set. + + while( _displayThread.getDisplay() == null ) { + try { Thread.sleep( 10 ) ; } catch( InterruptedException e ) {} + } + + _displayThread.getDisplay().syncExec( new Runnable() { + public void run() { + + _testShell = new Shell( _displayThread.getDisplay() ) ; + _testShell.setText( "Event Modifier Test NewtCanvasSWT" ) ; + _testShell.setLayout( new FillLayout() ) ; + + { + GLCapabilities caps = new GLCapabilities( GLProfile.get( GLProfile.GL2ES2 ) ) ; + _glWindow = GLWindow.create( caps ) ; + + NewtCanvasSWT.create( _testShell, SWT.NO_BACKGROUND, _glWindow ) ; + + _glWindow.addGLEventListener( new BigGreenXGLEventListener() ) ; + } + + _testShell.setBounds( TEST_FRAME_X, TEST_FRAME_Y, TEST_FRAME_WIDTH, TEST_FRAME_HEIGHT ) ; + _testShell.open() ; + } + } ) ; + + _glWindow.addMouseListener( _testMouseListener ) ; + } + + //////////////////////////////////////////////////////////////////////////// + + @AfterClass + public static void afterClass() throws Exception { + + TestNewtEventModifiers.afterClass() ; + + _glWindow.destroy() ; + + _displayThread.getDisplay().syncExec( new Runnable() { + public void run() { + _testShell.dispose() ; + } + } ) ; + + _displayThread.interrupt() ; + + try { + _displayThread.join() ; + } catch( InterruptedException e ) { + } + } + + //////////////////////////////////////////////////////////////////////////// + + public static void main(String args[]) throws Exception { + String testName = TestNewtEventModifiersNewtCanvasSWT.class.getName() ; + org.junit.runner.JUnitCore.main( testName ) ; + } + + //////////////////////////////////////////////////////////////////////////// +} |