Package com.jme3.swingGui

Source Code of com.jme3.swingGui.JMEDesktop$LightWeightPopup

/*
* Copyright (c) 2003-2009 jMonkeyEngine
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
*   notice, this list of conditions and the following disclaimer.
*
* * 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.
*
* * Neither the name of 'jMonkeyEngine' nor the names of its contributors
*   may be used to endorse or promote products derived from this software
*   without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "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 THE COPYRIGHT OWNER 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.
*/

package com.jme3.swingGui;

import java.awt.AWTEvent;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.Frame;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.KeyboardFocusManager;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.event.ContainerEvent;
import java.awt.event.ContainerListener;
import java.awt.event.FocusEvent;
import java.awt.event.InputEvent;
import java.awt.event.KeyEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseWheelEvent;
import java.beans.PropertyVetoException;
import java.lang.reflect.InvocationTargetException;
import java.util.HashMap;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.swing.JComponent;
import javax.swing.JDesktopPane;
import javax.swing.JInternalFrame;
import javax.swing.JPanel;
import javax.swing.JRootPane;
import javax.swing.JScrollPane;
import javax.swing.JTabbedPane;
import javax.swing.JViewport;
import javax.swing.Popup;
import javax.swing.PopupFactory;
import javax.swing.RepaintManager;
import javax.swing.SwingUtilities;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.plaf.basic.BasicInternalFrameUI;

import com.jme3.asset.AssetManager;
import com.jme3.input.InputManager;
import com.jme3.input.KeyInput;
import com.jme3.input.RawInputListener;
import com.jme3.input.event.JoyAxisEvent;
import com.jme3.input.event.JoyButtonEvent;
import com.jme3.input.event.KeyInputEvent;
import com.jme3.input.event.MouseButtonEvent;
import com.jme3.input.event.MouseMotionEvent;
import com.jme3.input.event.TouchEvent;
import com.jme3.material.Material;
import com.jme3.material.TechniqueDef.LightMode;
import com.jme3.math.Ray;
import com.jme3.math.Vector2f;
import com.jme3.math.Vector3f;
import com.jme3.renderer.RenderManager;
import com.jme3.scene.Geometry;
import com.jme3.scene.Node;
import com.jme3.scene.shape.Quad;
import com.jme3.swingGui.dnd.JMEDragAndDrop;
import com.jme3.system.AppSettings;
import com.jme3.system.JmeSystem;
import com.jme3.texture.Texture;
import com.jme3.texture.Texture2D;

/**
* A quad that displays a {@link JDesktopPane} as texture. It also converts jME mouse and keyboard events to Swing
* events. The latter does work for ortho mode only. There are some issues with using multiple of this desktops.
* <p>
* Notes
* <ul>
* <li> Only access the Swing UI from the Swing event dispatch thread! See {@link SwingUtilities#invokeLater}
* and <a href="http://java.sun.com/docs/books/tutorial/uiswing/concurrency/index.html">Swing tutorial</a> for details.</li>
* <li>If you would like to change the L&F take into account that PropertiesDialog and PropertiesDialog2 change
* the L&F setting upon invocation (you can e.g. change the L&F after the dialog).</li>
* </ul>
*
* @author Portet to jme3 by user starcom "Paul Kashofer Austria"
*
* @see ImageGraphics
*/
public class JMEDesktop extends Geometry {
    private static final Logger logger = Logger.getLogger(JMEDesktop.class
            .getName());
   
    private static final long serialVersionUID = 1L;
    private ImageGraphics graphics;
    private JDesktopPane desktop;
    private Texture texture;
    private boolean initialized;
    private int width;
    private int height;

    private boolean showingJFrame = false;
    private final Frame awtWindow;
    private int desktopWidth;
    private int desktopHeight;
    private static final int DOUBLE_CLICK_TIME = 300;
    private RawInputListener rawInputListener; // The Listener, which handles the Events;
    private InputManager inputManager; // The Parent InputManager
    private JMEDragAndDrop dragAndDropSupport;
    private AppSettings settings;
    private RenderManager renderer;
    private boolean disposing = false;

    /**
     * @return JMEDragAndDrop used for this desktop
     */
    public JMEDragAndDrop getDragAndDropSupport() {
        return dragAndDropSupport;
    }

    /**
     * @param dragAndDropSupport JMEDragAndDrop to be used for this desktop
     * @see JMEDragAndDrop#setDesktop(JMEDesktop)
     */
    public void setDragAndDropSupport( JMEDragAndDrop dragAndDropSupport ) {
        this.dragAndDropSupport = dragAndDropSupport;
    }

    /**
     * @see #setShowingJFrame
     * @return true if frame is displayed
     */
    public boolean isShowingJFrame() {
        return showingJFrame;
    }

    /**
     * @param showingJFrame true to display the desktop in a JFrame instead on this quad.
     * @deprecated for debuggin only
     */
    public void setShowingJFrame( boolean showingJFrame ) {
        this.showingJFrame = showingJFrame;
        awtWindow.setVisible( showingJFrame );
        awtWindow.repaint();
    }

    /**
     * Allows to disable input for the whole desktop and to add custom input actions.
     *
     * @return this desktops input hander for input bindings
     * @see #getXUpdateAction()
     * @see #getYUpdateAction()
     * @see #getWheelUpdateAction()
     * @see #getButtonUpdateAction(int)
     * @see #getKeyUpdateAction()
     */
    public InputManager getInputManager() {
        return inputManager;
    }

    /**
     * Create a quad with a Swing-Texture. Creates the quad and the JFrame but do not setup the rest.
     * Call {@link #setup(int, int, boolean, InputManager)} to finish setup.
     *
     * @param name name of this desktop
     */
    private JMEDesktop( String name ) {
        super(name);
/*
Quad quad = new Quad(2,2)
{
  @Override
  public void updateGeometry(float x, float y)
  {
    this.updateGeometry(x,y,false);
  }
  @Override
  public void updateGeometry(float x, float y, boolean flip)
  {
    super.updateGeometry(x,y,flip);

        setBuffer(Type.Position, 3, new float[]{0,      0,      0,
                                                width,  0,      0,
                                                width,  height, 0,
                                                0,      height, 0
                                                });
//    setBuffer(Type.Position, 3, new float[]{(-width / 2f),(height / 2f),0,   (-width / 2f),(-height / 2f),0,   (width / 2f),(-height / 2f),0,   (width / 2f),(height / 2f),0 });
  }
};
*/
//setMesh(quad);
setMesh(new Quad());

        awtWindow = new Frame() {
            private static final long serialVersionUID = 1L;
            public boolean isShowing() {
                return true;
            }

            public boolean isVisible() {
//                if ( new Throwable().getStackTrace()[1].getMethodName().startsWith( "requestFocus" ) ) {
//                    logger.info( "requestFocus" );
//                }

                if ( awtWindow.isFocusableWindow()
                        && new Throwable().getStackTrace()[1].getMethodName().startsWith( "requestFocus" ) ) {
                    return false;
                }
                return initialized || super.isVisible();
            }

            public Graphics getGraphics() {
                if ( !showingJFrame ) {
                    return graphics == null ? super.getGraphics() : graphics.create();
                }
               
                return super.getGraphics();
            }

            public boolean isFocused() {
                return true;
            }
        };
        awtWindow.setFocusableWindowState( false );
        Container contentPane = awtWindow;
        awtWindow.setUndecorated( true );
        dontDrawBackground( contentPane );
//            ( (JComponent) contentPane ).setOpaque( false );

        desktop = new JDesktopPane() {
            private static final long serialVersionUID = 1L;
            public void paint( Graphics g ) {
                if ( !isShowingJFrame() ) {
                    g.clearRect( 0, 0, getWidth(), getHeight() );
                }
                super.paint( g );
            }

            public boolean isOptimizedDrawingEnabled() {
                return false;
            }
        };

        new ScrollPaneRepaintFixListener().addTo( desktop );


        final Color transparent = new Color( 0, 0, 0, 0 );
        desktop.setBackground( transparent );
        desktop.setFocusable( true );
        desktop.addMouseListener( new MouseAdapter() {
            public void mousePressed( MouseEvent e ) {
                desktop.requestFocusInWindow();
            }
        } );

        // this internal frame is a workaround for key binding problems in JDK1.5
        // todo: this workaround does not seem to work on mac
        if ( System.getProperty( "os.name" ).toLowerCase().indexOf( "mac" ) < 0 ) {
            final JInternalFrame internalFrame = new JInternalFrame();
            internalFrame.setUI( new BasicInternalFrameUI( internalFrame ) {
                protected void installComponents() {
                }
            } );
            internalFrame.setOpaque( false );
            internalFrame.setBackground( null );
            internalFrame.getContentPane().setLayout( new BorderLayout() );
            internalFrame.getContentPane().add( desktop, BorderLayout.CENTER );
            internalFrame.setVisible( true );
            internalFrame.setBorder( null );
            contentPane.add( internalFrame );
        }
        else {
            // this would have suited for JDK1.4:
            contentPane.add( desktop, BorderLayout.CENTER );
        }

        awtWindow.pack();

        RepaintManager.currentManager( null ).setDoubleBufferingEnabled( false );
    }

    /**
     * Create a quad with a Swing-Texture.
     * Note that for the texture a width and height that is a power of 2 is used if the graphics card does
     * not support the specified size for textures. E.g. this results in a 1024x512
     * texture for a 640x480 desktop (consider using a 512x480 desktop in that case).
     *
     * @param name               name of the spatial
     * @param width              desktop width
     * @param height             desktop height
     * @param inputHandlerParent InputManager where the InputListener of this desktop should be added as subhandler.
     * @see #getInputManager()
     */
    public JMEDesktop( String name, final int width, final int height, InputManager inputHandlerParent , AppSettings settings, RenderManager renderer) {
        this( name, width, height, false, inputHandlerParent, settings, renderer );
    }

    /**
     * Create a quad with a Swing-Texture.
     * Note that for the texture a width and height that is a power of 2 is used if the graphics card does
     * not support the specified size for textures or mipMapping is true. E.g. this results in a 1024x512
     * texture for a 640x480 desktop (consider using a 512x480 desktop in that case).
     *
     * @param name               name of the spatial
     * @param width              desktop width
     * @param height             desktop hieght
     * @param mipMapping         true to compute mipmaps for the desktop (not recommended), false for creating
     *                           a single image texture
     * @param inputHandlerParent InputManager where the InputListener of this desktop should be added as subhandler.
     * @see #getInputManager()
     */
    public JMEDesktop( String name, final int width, final int height, boolean mipMapping, InputManager inputHandlerParent, AppSettings settings, RenderManager renderer ) {
        this( name, width, height, 1280, 720, mipMapping, inputHandlerParent, settings, renderer );
    }

    /**
     * Create a quad with a Swing-Texture.
     * Note that for the texture a width and height that is a power of 2 is used if the graphics card does
     * not support the specified size for textures or mipMapping is true. E.g. this results in a 1024x512
     * texture for a 640x480 desktop (consider using a 512x480 desktop in that case).
     *
     * @param name               name of the spatial
     * @param width              desktop width
     * @param height             desktop hieght
     * @param scaleFromWidth     default width, that will be scaled
     * @param scaleFromHeight    default height, that will be scaled
     * @param mipMapping         true to compute mipmaps for the desktop (not recommended), false for creating
     *                           a single image texture
     * @param inputHandlerParent InputManager where the InputListener of this desktop should be added as subhandler.
     * @see #getInputManager()
     */
    public JMEDesktop( String name, final int width, final int height, int scaleFromWidth, int scaleFromHeight, boolean mipMapping, InputManager inputHandlerParent, AppSettings settings, RenderManager renderer ) {
        this( name );
        this.renderer = renderer;
        setup( width, height, scaleFromWidth, scaleFromHeight, mipMapping, inputHandlerParent, settings );
    }

    /**
     * Set up the desktop quad - may be called only once.
     * Note that for the texture a width and height that is a power of 2 is used if the graphics card does
     * not support the specified size for textures or mipMapping is true. E.g. this results in a 1024x512
     * texture for a 640x480 desktop (consider using a 512x480 desktop in that case).
     *
     * @param width              desktop width
     * @param height             desktop hieght
     * @param mipMapping         true to compute mipmaps for the desktop (not recommended), false for creating
     *                           a single image texture
     * @param inputHandlerParent InputManager where the InputListener of this desktop should be added as subhandler,
     * @see #getInputManager()
     */
    private void setup( int width, int height, int unscaledWidth, int unscaledHeight, boolean mipMapping, InputManager inputHandlerParent, AppSettings settings ) {
//        reconstruct( null, null, null, null );
        if ( inputHandlerParent == null ) {
            throw new IllegalStateException( "InputManager must not be null!" );
        }

        if ( initialized ) {
            throw new IllegalStateException( "may be called only once" );
        }
        inputManager = inputHandlerParent;
        this.settings = settings;
        initInputActions(inputHandlerParent);
        ((Quad)getMesh()).updateGeometry( powerOf2SizeIfNeeded( unscaledWidth, mipMapping ), powerOf2SizeIfNeeded( unscaledHeight, mipMapping ), true );

        this.width = powerOf2SizeIfNeeded( unscaledWidth, mipMapping );
        this.height = powerOf2SizeIfNeeded( unscaledHeight, mipMapping );
//        setModelBound( new BoundingBox() );
//        updateModelBound();

        desktop.setPreferredSize( new Dimension( unscaledWidth, unscaledHeight ) );
        desktopWidth = unscaledWidth;
        desktopHeight = unscaledHeight;
        awtWindow.pack();


//        TextureState ts = DisplaySystem.getDisplaySystem().getRenderer().createTextureState();
//        ts.setCorrectionType( TextureState.CorrectionType.Perspective );
        texture = new Texture2D();
        texture.setMagFilter( Texture.MagFilter.Bilinear );
        texture.setMinFilter( mipMapping ? Texture.MinFilter.Trilinear : Texture.MinFilter.BilinearNoMipMaps );
        texture.setWrap( Texture.WrapMode.Repeat );

        graphics = ImageGraphics.createInstance( this.width, this.height, mipMapping ? 2 : 0 , settings, renderer.getRenderer());
        enableAntiAlias( graphics );
        graphics.translate( ( this.width - unscaledWidth ) * 0.5f, ( this.height - unscaledHeight ) * 0.5f );
        texture.setImage( graphics.getImage() );

//        texture.setScale( new Vector3f( 1, -1, 1 ) );
//        ts.setTexture( texture );
//        this.setRenderState( ts );


        AssetManager assetManager = JmeSystem.newAssetManager(Thread.currentThread().getContextClassLoader().getResource("com/jme3/asset/Desktop.cfg"));
        Material mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
        mat.setTransparent(true);
        mat.getAdditionalRenderState().setBlendMode(com.jme3.material.RenderState.BlendMode.Alpha);
        mat.selectTechnique("Default",renderer);
        mat.getActiveTechnique().getDef().setLightMode(LightMode.Disable);
        mat.setTexture("ColorMap", texture);
        setMaterial(mat);
        setQueueBucket(com.jme3.renderer.queue.RenderQueue.Bucket.Gui);
//setQueueBucket(com.jme3.renderer.queue.RenderQueue.Bucket.Transparent);
//getLocalScale().set(new Vector3f(0.5f,0.5f,0.5f)); //Nur zum Testen

//        mat = new Material(assetManager, "Common/MatDefs/Misc/ColoredTextured.j3md");
//        mat.getActiveTechnique().getDef().setLightMode(LightMode.Disable);
//        mat.setTexture("ColorMap", texture);

/*
        BlendState alpha = DisplaySystem.getDisplaySystem().getRenderer().createBlendState();
        alpha.setEnabled( true );
        alpha.setBlendEnabled( true );
        alpha.setSourceFunction( BlendState.SourceFunction.SourceAlpha );
        alpha.setDestinationFunction( BlendState.DestinationFunction.OneMinusSourceAlpha );
        alpha.setTestEnabled( true );
        alpha.setTestFunction( BlendState.TestFunction.GreaterThan );
        this.setRenderState( alpha );
*/

//        Toolkit.getDefaultToolkit().addAWTEventListener( new AWTEventListener() {
//            public void eventDispatched( AWTEvent event ) {
//                if ( isShowingJFrame() ) {
//                    logger.info( event );
//                }
//            }
//        }, 0xFFFFFFFFFFFFFFFFl );


        if ( desktopsUsed == 0 ) {
            PopupFactory.setSharedInstance( new MyPopupFactory() );
        }
        desktopsUsed++;

        SwingUtilities.invokeLater( new Runnable() {
            public void run() {
                if (!disposing)JMEDesktop.this.setFocusOwner( desktop );
            }
        } );

        initialized = true;

        setSynchronizingThreadsOnUpdate( true );
        setLocalScale(((float)width/(float)unscaledWidth),((float)height/(float)unscaledHeight),1.0f);
    }

  private void initInputActions(InputManager manager)
  {
    inputManager = manager;
    rawInputListener = new RawInputListener()
    {
      @Override
      public void beginInput() {}
      @Override
      public void endInput() {}
      @Override
      public void onJoyAxisEvent(JoyAxisEvent evt) {}
      @Override
      public void onJoyButtonEvent(JoyButtonEvent evt) {}
      @Override
      public void onMouseMotionEvent(MouseMotionEvent evt)
      {
        onMove(evt.getDX(), evt.getDY(), evt.getX(), evt.getY());
        onWheel(evt.getDeltaWheel(), evt.getX(), evt.getY());
      }
      @Override
      public void onMouseButtonEvent(MouseButtonEvent evt)
      {
            setButtonDown(evt.getButtonIndex(), evt.isPressed());
            onButton( evt.getButtonIndex(), evt.isPressed(), lastXin, lastYin );
      }
      @Override
      public void onKeyEvent(KeyInputEvent evt)
      {
        setKeyDown(evt.getKeyCode(), evt.isPressed());
        onKey( evt.getKeyChar(), evt.getKeyCode(), evt.isPressed());
      }
  @Override
  public void onTouchEvent(TouchEvent evt) {
    // TODO Auto-generated method stub
   
  }
    };
    manager.addRawInputListener(rawInputListener);
  }

    private static int desktopsUsed = 0;


    //todo: reuse the runnables
    //todo: possibly reuse events, too?

    public void onKey( final char character, final int keyCode, final boolean pressed ) {
        try {
            SwingUtilities.invokeAndWait( new Runnable() {
                public void run() {
                    if (!disposing)sendAWTKeyEvent( keyCode, pressed, character );
                }
            } );
        } catch ( InterruptedException e ) {
            logger.logp(Level.SEVERE, this.getClass().toString(),
                    "onKey(character, keyCode, pressed)", "Exception", e);
        } catch ( InvocationTargetException e ) {
            logger.logp(Level.SEVERE, this.getClass().toString(),
                    "onKey(character, keyCode, pressed)", "Exception", e);
        }
    }

    public void onButton( final int swingButton, final boolean pressed, final int x, final int y ) {
        convert( x, y, location );
        final int awtX = (int) location.x;
        final int awtY = (int) location.y;
        final int awtSwingButton = convertButton(swingButton);
        try {
            SwingUtilities.invokeAndWait( new Runnable() {
                public void run() {
                    if (!disposing)sendAWTMouseEvent( awtX, awtY, pressed, awtSwingButton );
                }
            } );
        } catch ( InterruptedException e ) {
            logger.logp(Level.SEVERE, this.getClass().toString(),
                    "onButton(swingButton, pressed, x, y)", "Exception", e);
        } catch ( InvocationTargetException e ) {
            logger.logp(Level.SEVERE, this.getClass().toString(),
                    "onButton(swingButton, pressed, x, y)", "Exception", e);
        }
    }

    public void onWheel( final int wheelDelta, final int x, final int y ) {
        convert( x, y, location );
        final int awtX = (int) location.x;
        final int awtY = (int) location.y;
        try {
            SwingUtilities.invokeAndWait( new Runnable() {
                public void run() {
                    if (!disposing)sendAWTWheelEvent( wheelDelta, awtX, awtY );
                }
            } );
        } catch ( InterruptedException e ) {
            logger.logp(Level.SEVERE, this.getClass().toString(),
                    "onWheel(wheelDelta, x, y)", "Exception", e);
        } catch ( InvocationTargetException e ) {
            logger.logp(Level.SEVERE, this.getClass().toString(),
                    "onWheel(wheelDelta, x, y)", "Exception", e);
        }
    }

    public void onMove( int xDelta, int yDelta, final int newX, final int newY ) {
        convert( newX, newY, location );
        final int awtX = (int) location.x;
        final int awtY = (int) location.y;
        try {
            SwingUtilities.invokeAndWait( new Runnable() {
                public void run() {
                    if (!disposing)sendAWTMouseEvent( awtX, awtY, false, MouseEvent.NOBUTTON );
                }
            } );
        } catch ( InterruptedException e ) {
            logger.logp(Level.SEVERE, this.getClass().toString(),
                    "onMove(xDelta, yDelta, newX, newY)", "Exception", e);
        } catch ( InvocationTargetException e ) {
            logger.logp(Level.SEVERE, this.getClass().toString(),
                    "onMove(xDelta, yDelta, newX, newY)", "Exception", e);
        }
    }

    private boolean synchronizingThreadsOnUpdate;

    /**
     * @return true if update and swing thread should be synchronized (avoids flickering, eats some performance)
     */
    public boolean isSynchronizingThreadsOnUpdate() {
        return synchronizingThreadsOnUpdate;
    }

    /**
     * Choose if update and swing thread should be synchronized (avoids flickering, eats some performance)
     *
     * @param synchronizingThreadsOnUpdate true to synchronize
     */
    public void setSynchronizingThreadsOnUpdate( boolean synchronizingThreadsOnUpdate ) {
        if ( this.synchronizingThreadsOnUpdate != synchronizingThreadsOnUpdate ) {
            this.synchronizingThreadsOnUpdate = synchronizingThreadsOnUpdate;
        }
    }

    private void enableAntiAlias( Graphics2D graphics ) {
        RenderingHints hints = graphics.getRenderingHints();
        if ( hints == null ) {
            hints = new RenderingHints( RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON );
        }
        else {
            hints.put( RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON );
        }
        graphics.setRenderingHints( hints );
    }

    public Vector2f getMousePosition()
    {
      return new Vector2f((float)lastXin, (float)lastYin);
    }

    private static class LightWeightPopup extends Popup {
        private static final Integer INTEGER_MAX_VALUE = Integer.MAX_VALUE;

        public LightWeightPopup( JComponent desktop ) {
            this.desktop = desktop;
            new ScrollPaneRepaintFixListener().addTo( panel );
        }

        private final JComponent desktop;

        JPanel panel = new JPanel( new BorderLayout() );

        public void adjust( Component owner, Component contents, int x, int y ) {
            panel.setVisible( false );
            desktop.add( panel, INTEGER_MAX_VALUE );
            panel.removeAll();
            panel.add( contents, BorderLayout.CENTER );
            if ( contents instanceof JComponent ) {
                JComponent jComponent = (JComponent) contents;
                jComponent.setDoubleBuffered( false );
            }
            panel.setSize( panel.getPreferredSize() );
            y = Math.min( y, desktop.getHeight() - panel.getHeight() );
            x = Math.min( x, desktop.getWidth() - panel.getWidth() );
            panel.setLocation( x, y );
            contents.invalidate();
            panel.validate();
        }

        public void show() {
            panel.setVisible( true );
        }

        public void hide() {
            Rectangle bounds = panel.getBounds();
            desktop.remove( panel );
            desktop.repaint( bounds );
        }
    }

    private void sendAWTKeyEvent( int keyCode, boolean pressed, char character ) {
        keyCode = AWTKeyInput.toAWTCode( keyCode );
        if ( keyCode != 0 ) {
            Component focusOwner = getFocusOwner();
            if ( focusOwner == null ) {
                focusOwner = desktop;
            }
            if ( character == '\0' ) {
                character = KeyEvent.CHAR_UNDEFINED;
            }
            if ( focusOwner != null ) {
                if ( pressed ) {
                    KeyEvent event = new KeyEvent( focusOwner, KeyEvent.KEY_PRESSED,
                            System.currentTimeMillis(), getCurrentModifiers( -1 ),
                            keyCode, character );
                    dispatchEvent( focusOwner, event );
                    anInt.value = keyCode;
                    Char c = characters.get( anInt );
                    if ( c == null ) {
                        characters.put( new Int( keyCode ), new Char( character ) );
                    }
                    else {
                        c.value = character;
                    }
                    if ( character != KeyEvent.CHAR_UNDEFINED ) {
                        dispatchEvent( focusOwner, new KeyEvent( focusOwner, KeyEvent.KEY_TYPED,
                                System.currentTimeMillis(), getCurrentModifiers( -1 ),
                                0, character ) );
                    }
                }
                if ( !pressed ) {
                    anInt.value = keyCode;
                    Char c = characters.get( anInt );
                    if ( c != null ) {
                        character = c.value;
                        //TODO: repeat input
//                        if ( character != KeyEvent.CHAR_UNDEFINED ) {
//                            dispatchEvent( focusOwner, new KeyEvent( focusOwner, KeyEvent.KEY_TYPED,
//                                    System.currentTimeMillis(), getCurrentModifiers( -1 ),
//                                    0, character ) );
//                        }
                    }
                    dispatchEvent( focusOwner, new KeyEvent( focusOwner, KeyEvent.KEY_RELEASED,
                            System.currentTimeMillis(), getCurrentModifiers( -1 ),
                            keyCode, character ) );
                }
            }
        }
    }

    private void dispatchEvent( final Component receiver, final AWTEvent event ) {
        if ( getModalComponent() == null || SwingUtilities.isDescendingFrom( receiver, getModalComponent() ) ) {
            if ( !SwingUtilities.isEventDispatchThread() ) {
                throw new IllegalStateException( "not in swing thread!" );
            }
            receiver.dispatchEvent( event );
        }
    }

    private static Int anInt = new Int( 0 );

    private static class Int {
        public Int( int value ) {
            this.value = value;
        }

        public boolean equals( Object obj ) {
            return obj instanceof Int && ( (Int) obj ).value == value;

        }

        public int hashCode() {
            return value;
        }

        int value;
    }

    private static class Char {
        public Char( char value ) {
            this.value = value;
        }

        char value;
    }

    /**
     * From keyCode (Int) to character (Char)
     */
    private Map<Int,Char> characters = new HashMap<Int,Char>();

    private static void dontDrawBackground( Container container ) {
        if ( container != null ) {
            container.setBackground( null );
            if ( container instanceof JComponent ) {
                final JComponent component = ( (JComponent) container );
                component.setOpaque( false );
            }
            dontDrawBackground( container.getParent() );
        }
    }

    private static int powerOf2SizeIfNeeded( int size, boolean generateMipMaps ) {
        if ( generateMipMaps ) { // || !TextureState.isNonPowerOfTwoTextureSupported() ) {
            int powerOf2Size = 1;
            while ( powerOf2Size < size ) {
                powerOf2Size <<= 1;
            }
            return powerOf2Size;
        }
       
        return size;      
    }

    private Component lastComponent;
    private Component grabbedMouse;
    private int grabbedMouseButton;
    private int downX = 0;
    private int downY = 0;
    private long lastClickTime = 0;
    private int clickCount = 0;
    private static final int MAX_CLICKED_OFFSET = 4;

    private Vector2f location = new Vector2f();

    private void sendAWTWheelEvent( int wheelDelta, int x, int y ) {
        Component comp = lastComponent != null ? lastComponent : componentAt( x, y, desktop, false );
        if ( comp == null ) {
            comp = desktop;
        }
        final Point pos = convertPoint( desktop, x, y, comp );
        final MouseWheelEvent event = new MouseWheelEvent( comp,
                MouseEvent.MOUSE_WHEEL,
                System.currentTimeMillis(), getCurrentModifiers( -1 ), pos.x, pos.y, 1, false,
                MouseWheelEvent.WHEEL_UNIT_SCROLL,
                Math.abs( wheelDelta ), wheelDelta > 0 ? -1 : 1 );
        dispatchEvent( comp, event );
    }

    private boolean useConvertPoint = true;

    private Point convertPoint( Component parent, int x, int y, Component comp ) {
        if ( useConvertPoint ) {
            try {
                return SwingUtilities.convertPoint( parent, x, y, comp );
            } catch ( InternalError e ) {
                useConvertPoint = false;
            }
        }
        if ( comp != null ) {
            while ( comp != parent ) {
                x -= comp.getX();
                y -= comp.getY();
                if ( comp.getParent() == null ) {
                    break;
                }
                comp = comp.getParent();
            }
        }
        return new Point( x, y );
    }

    private void sendAWTMouseEvent( int x, int y, boolean pressed, int swingButton ) {
        Component comp = componentAt( x, y, desktop, false );

        final int eventType;
        if ( swingButton > MouseEvent.NOBUTTON ) {
            eventType = pressed ? MouseEvent.MOUSE_PRESSED : MouseEvent.MOUSE_RELEASED;
        }
        else {
            eventType = getButtonMask( MouseEvent.NOBUTTON ) == 0 ? MouseEvent.MOUSE_MOVED : MouseEvent.MOUSE_DRAGGED;
        }

        final long time = System.currentTimeMillis();
        if ( lastComponent != comp ) {
            //enter/leave events
            while ( lastComponent != null && ( comp == null || !SwingUtilities.isDescendingFrom( comp, lastComponent ) ) )
            {
                final Point pos = convertPoint( desktop, x, y, lastComponent );
                sendExitedEvent( lastComponent, getCurrentModifiers( swingButton ), pos );
                lastComponent = lastComponent.getParent();
            }
            final Point pos = convertPoint( desktop, x, y, lastComponent );
            if ( lastComponent == null ) {
                lastComponent = desktop;
            }
            sendEnteredEvent( comp, lastComponent, getCurrentModifiers( swingButton ), pos );
            lastComponent = comp;
            downX = Integer.MIN_VALUE;
            downY = Integer.MIN_VALUE;
            lastClickTime = 0;
        }

        if ( comp != null ) {
            boolean clicked = false;
            if ( swingButton > MouseEvent.NOBUTTON ) {
                if ( pressed ) {
                    grabbedMouse = comp;
                    grabbedMouseButton = swingButton;
                    downX = x;
                    downY = y;
                    setFocusOwner( componentAt( x, y, desktop, true ) );
                }
                else if ( grabbedMouseButton == swingButton && grabbedMouse != null ) {
                    comp = grabbedMouse;
                    grabbedMouse = null;
                    if ( Math.abs( downX - x ) <= MAX_CLICKED_OFFSET && Math.abs( downY - y ) < MAX_CLICKED_OFFSET ) {
                        if ( lastClickTime + DOUBLE_CLICK_TIME > time ) {
                            clickCount++;
                        }
                        else {
                            clickCount = 1;
                        }
                        clicked = true;
                        lastClickTime = time;
                    }
                    downX = Integer.MIN_VALUE;
                    downY = Integer.MIN_VALUE;
                }
            }
            else if ( grabbedMouse != null ) {
                comp = grabbedMouse;
            }

            final Point pos = convertPoint( desktop, x, y, comp );
            final MouseEvent event = new MouseEvent( comp,
                    eventType,
                    time, getCurrentModifiers( swingButton ), pos.x, pos.y, clickCount,
                    swingButton == MouseEvent.BUTTON2 && pressed, // todo: should this be platform dependent? (e.g. mac)
                    swingButton >= 0 ? swingButton : 0 );
            dispatchEvent( comp, event );
            if ( clicked ) {
                // CLICKED seems to need special glass pane handling o_O
                comp = componentAt( x, y, desktop, true );
                final Point clickedPos = convertPoint( desktop, x, y, comp );

                final MouseEvent clickedEvent = new MouseEvent( comp,
                        MouseEvent.MOUSE_CLICKED,
                        time, getCurrentModifiers( swingButton ), clickedPos.x, clickedPos.y, clickCount,
                        false, swingButton );
                dispatchEvent( comp, clickedEvent );
            }
        }
        else if ( pressed ) {
            // clicked no component at all
            setFocusOwner( null );
        }
    }

    private boolean focusCleared = false;

    public void setFocusOwner( Component comp ) {
        if ( comp == null || comp.isFocusable() ) {
            for ( Component p = comp; p != null; p = p.getParent() ) {
                if ( p instanceof JInternalFrame ) {
                    try {
                        ( (JInternalFrame) p ).setSelected( true );
                    } catch ( PropertyVetoException e ) {
                        logger.logp(Level.SEVERE, this.getClass().toString(),
                                "setFocusOwner(Component comp)", "Exception", e);
                    }
                }
            }
            awtWindow.setFocusableWindowState( true );
            Component oldFocusOwner = getFocusOwner();
            if ( comp == desktop ) {
                comp = null;
            }
            if ( oldFocusOwner != comp ) {
                if ( oldFocusOwner != null ) {
                    dispatchEvent( oldFocusOwner, new FocusEvent( oldFocusOwner,
                            FocusEvent.FOCUS_LOST, false, comp ) );
                }
                KeyboardFocusManager.getCurrentKeyboardFocusManager().clearGlobalFocusOwner();
                if ( comp != null ) {
                    dispatchEvent( comp, new FocusEvent( comp,
                            FocusEvent.FOCUS_GAINED, false, oldFocusOwner ) );
                }
            }
            awtWindow.setFocusableWindowState( false );
        }
        focusCleared = comp == null;
    }

    private int getCurrentModifiers( int swingBtton ) {
        int modifiers = 0;
        if ( isKeyDown( KeyInput.KEY_LMENU ) ) {
            modifiers |= InputEvent.ALT_DOWN_MASK;
            modifiers |= InputEvent.ALT_MASK;
        }
        if ( isKeyDown( KeyInput.KEY_RMENU ) ) {
            modifiers |= InputEvent.ALT_GRAPH_DOWN_MASK;
            modifiers |= InputEvent.ALT_GRAPH_MASK;
        }
        if ( isKeyDown( KeyInput.KEY_LCONTROL ) || isKeyDown( KeyInput.KEY_RCONTROL ) ) {
            modifiers |= InputEvent.CTRL_DOWN_MASK;
            modifiers |= InputEvent.CTRL_MASK;
        }
        if ( isKeyDown( KeyInput.KEY_LSHIFT ) || isKeyDown( KeyInput.KEY_RSHIFT ) ) {
            modifiers |= InputEvent.SHIFT_DOWN_MASK;
            modifiers |= InputEvent.SHIFT_MASK;
        }
        return modifiers | getButtonMask( swingBtton );
    }


    boolean butLMenuDown = false;
    boolean butRMenuDown = false;
    boolean butLControlDown = false;
    boolean butRControlDown = false;
    boolean butLShiftDown = false;
    boolean butRShiftDown = false;

    private boolean isKeyDown( int key )
    {
      if (key==KeyInput.KEY_LMENU) {return butLMenuDown;}
      else if (key==KeyInput.KEY_RMENU) {return butRMenuDown;}
      else if (key==KeyInput.KEY_LCONTROL) {return butLControlDown;}
      else if (key==KeyInput.KEY_RCONTROL) {return butRControlDown;}
      else if (key==KeyInput.KEY_LSHIFT) {return butLShiftDown;}
      else if (key==KeyInput.KEY_RSHIFT) {return butRShiftDown;}
      System.err.println("JMEDesktop.java: Key not supported: "+key);
      return false;
    }

    private void setKeyDown( int key, boolean down )
    {
      if (key==KeyInput.KEY_LMENU) {butLMenuDown = down;}
      else if (key==KeyInput.KEY_RMENU) {butRMenuDown = down;}
      else if (key==KeyInput.KEY_LCONTROL) {butLControlDown = down;}
      else if (key==KeyInput.KEY_RCONTROL) {butRControlDown = down;}
      else if (key==KeyInput.KEY_LSHIFT) {butLShiftDown = down;}
      else if (key==KeyInput.KEY_RSHIFT) {butRShiftDown = down;}
    }

    boolean m1 = false;
    boolean m2 = false;
    boolean m3 = false;
    private void setButtonDown( int mouseBut, boolean down)
    {
      if (mouseBut == 0) { m1 = down; }
      if (mouseBut == 1) { m2 = down; }
      if (mouseBut == 2) { m3 = down; }
    }

    private boolean isButtonDown( int mouseBut )
    {
      if (mouseBut == 0) { return m1; }
      if (mouseBut == 1) { return m2; }
      if (mouseBut == 2) { return m3; }
      System.err.println("JMEDesktop.java: Button not supported: "+mouseBut);
      return false;
    }

    private int getButtonMask( int swingButton ) {
        int buttonMask = 0;
        if ( isButtonDown( 0 ) || swingButton == MouseEvent.BUTTON1 ) {
            buttonMask |= InputEvent.BUTTON1_MASK;
            buttonMask |= InputEvent.BUTTON1_DOWN_MASK;
        }
        if ( isButtonDown( 1 ) || swingButton == MouseEvent.BUTTON2 ) {
            buttonMask |= InputEvent.BUTTON2_MASK;
            buttonMask |= InputEvent.BUTTON2_DOWN_MASK;
        }
        if ( isButtonDown( 2 ) || swingButton == MouseEvent.BUTTON3 ) {
            buttonMask |= InputEvent.BUTTON3_MASK;
            buttonMask |= InputEvent.BUTTON3_DOWN_MASK;
        }
        return buttonMask;
    }

    private int convertButton(int mouseBut)
    {
      if (mouseBut == 0) { return MouseEvent.BUTTON1; }
      if (mouseBut == 1) { return MouseEvent.BUTTON2; }
      if (mouseBut == 2) { return MouseEvent.BUTTON3; }
      System.err.println("JMEDesktop.java - convertButton(): Button not supported: "+mouseBut);
      return mouseBut;
    }

    private int lastXin = -1;
    private int lastXout = -1;
    private int lastYin = -1;
    private int lastYout = -1;

    private Ray pickRay = new Ray();
    private Vector3f bottomLeft = new Vector3f();
    private Vector3f topLeft = new Vector3f();
    private Vector3f topRight = new Vector3f();
    private Vector3f bottomRight = new Vector3f();
    private Vector3f tuv = new Vector3f();

    /**
     * Convert mouse coordinates from jME screen to JMEDesktop coordinates (Swing).
     * @param x jME x coordinate
     * @param y jME y coordinate
     * @param store resulting JDesktop coordinates
     */
    public void convert( int x, int y, Vector2f store ) {
        if ( lastXin == x && lastYin == y ) {
            store.x = lastXout;
            store.y = lastYout;
        }
        else {
            lastXin = x;
            lastYin = y;
logger.fine("Warning in JMEDesktop.java: Gehe von QUEUE_ORTHO aus! (assume QUEUE_ORTHO)");
//            if ( getRenderQueueMode() == Renderer.QUEUE_ORTHO ) {
                //TODO: occlusion by other quads (JMEFrames)
x = (int) (x * (desktopWidth/(desktopWidth*getLocalScale().x)));
y = (int) (desktopHeight - (y * (desktopHeight/(desktopHeight*getLocalScale().y))));
//                x = (int) ( x - getWorldTranslation().x  );
//                y = (int) (  ( (desktopHeight-y) - getWorldTranslation().y ) );
//            }
/*
            else {
                store.set( x, y );
                DisplaySystem.getDisplaySystem().getWorldCoordinates( store, 0, pickRay.origin );
                DisplaySystem.getDisplaySystem().getWorldCoordinates( store, 0.3f, pickRay.direction ).subtractLocal( pickRay.origin ).normalizeLocal();

                applyWorld( bottomLeft.set( -width * 0.5f, -height * 0.5f, 0 ) );
                applyWorld( topLeft.set( -width * 0.5f, height * 0.5f, 0 ) );
                applyWorld( topRight.set( width * 0.5f, height * 0.5f, 0 ) );
                applyWorld( bottomRight.set( width * 0.5f, -height * 0.5f, 0 ) );

                if ( pickRay.intersectWherePlanarQuad( topLeft, topRight, bottomLeft, tuv ) ) {
                    x = (int) ( ( tuv.y - 0.5f ) * width ) + desktopWidth / 2;
                    y = (int) ( ( tuv.z - 0.5f ) * height ) + desktopHeight / 2;
                }
                else {
                    x = -1;
                    y = -1;
                }
            }
*/
            lastYout = y;
            lastXout = x;

            store.set( x, y );
        }
    }

    private void applyWorld( Vector3f point ) {
        getWorldRotation().multLocal( point.multLocal( getWorldScale() ) ).addLocal( getWorldTranslation() );
    }

    /**
     * Find a component at specified desktop position.
     *
     * @param x x coordinate in Swing coordinate space
     * @param y y coordinate in Swing coordinate space
     * @return the top most component at specified location, null if no child component is found at that location
     */
    public Component componentAt( int x, int y ) {
        Component component = componentAt( x, y, desktop, true );
        if ( component != desktop ) {
            return component;
        }
      
        return null;       
    }

    private Component componentAt( int x, int y, Component parent, boolean scanRootPanes ) {
        if ( scanRootPanes && parent instanceof JRootPane ) {
            JRootPane rootPane = (JRootPane) parent;
            parent = rootPane.getContentPane();
        }

        Component child = parent;
        if ( !parent.contains( x, y ) ) {
            child = null;
        }
        else {
            synchronized ( parent.getTreeLock() ) {
                if ( parent instanceof Container ) {
                    Container container = (Container) parent;
                    int ncomponents = container.getComponentCount();
                    for ( int i = 0; i < ncomponents; i++ ) {
                        Component comp = container.getComponent( i );
                        if ( comp != null
                                && comp.isVisible()
                                && ( dragAndDropSupport == null || !dragAndDropSupport.isDragPanel(comp) )
                                && comp.contains( x - comp.getX(), y - comp.getY() ) ) {
                            child = comp;
                            break;
                        }
                    }
                }
            }
        }

        if ( child != null ) {
            if ( parent instanceof JTabbedPane && child != parent ) {
                child = ( (JTabbedPane) parent ).getSelectedComponent();
            }
            x -= child.getX();
            y -= child.getY();
        }
        return child != parent && child != null ? componentAt( x, y, child, scanRootPanes ) : child;
    }

    private void sendEnteredEvent( Component comp, Component lastComponent, int buttonMask, Point pos ) {
        if ( comp != null && comp != lastComponent ) {
            sendEnteredEvent( comp.getParent(), lastComponent, buttonMask, pos );

            pos = convertPoint( lastComponent, pos.x, pos.y, comp );
            final MouseEvent event = new MouseEvent( comp,
                    MouseEvent.MOUSE_ENTERED,
                    System.currentTimeMillis(), buttonMask, pos.x, pos.y, 0, false, 0 );
            dispatchEvent( comp, event );
        }

    }

    private void sendExitedEvent( Component lastComponent, int buttonMask, Point pos ) {
        final MouseEvent event = new MouseEvent( lastComponent,
                MouseEvent.MOUSE_EXITED,
                System.currentTimeMillis(), buttonMask, pos.x, pos.y, 1, false, 0 );
        dispatchEvent( lastComponent, event );
    }

    private final LockRunnable paintLockRunnable = new LockRunnable();

    @Override
    public void updateGeometricState() {
        if ( graphics.isDirty() ) {
            final boolean synchronizingThreadsOnUpdate = this.synchronizingThreadsOnUpdate;
            if ( synchronizingThreadsOnUpdate ) {
                synchronized ( paintLockRunnable ) {
                    try {
                        paintLockRunnable.wait = true;
                        if (!disposing)SwingUtilities.invokeLater( paintLockRunnable );
                        paintLockRunnable.wait( 100 );
                    } catch ( InterruptedException e ) {
                        logger.logp(Level.SEVERE, this.getClass().toString(), "draw(Renderer r)", "Exception", e);
                    }
                }
            }
            try {
                if ( graphics != null ) { // && texture.getTextureId() > 0) {
                    graphics.update( texture );
                }
            } finally {

                if ( synchronizingThreadsOnUpdate ) {
                    synchronized ( paintLockRunnable ) {
                        paintLockRunnable.notifyAll();
                    }
                }
            }
        }
texture.getImage().setUpdateNeeded();
//getLocalTranslation().set(starcom.jme3d.Referenz.view.getLocation().add(starcom.jme3d.Referenz.view.getDirection()));
//getLocalRotation().set(starcom.jme3d.Referenz.view.getRotation());
        super.updateGeometricState();
    }

    public JDesktopPane getJDesktop() {
        return desktop;
    }

    public Component getFocusOwner() {
        if ( !focusCleared ) {
            return this.awtWindow.getFocusOwner();
        }
      
        return null;       
    }

    private class LockRunnable implements Runnable {
        private boolean wait = false;

        public void run() {
            synchronized ( paintLockRunnable ) {
                notifyAll();
                if ( wait ) {
                    try {
                        //wait for repaint to finish
                        wait = false;
                        paintLockRunnable.wait( 200 );
                    } catch ( InterruptedException e ) {
                        logger.logp(Level.SEVERE, this.getClass().toString(),
                                "run()", "Exception", e);
                    }
                }
            }
        }
    }

    private static class MyPopupFactory extends PopupFactory {
        private final PopupFactory defaultPopupFactory = new PopupFactory();

        public Popup getPopup( Component owner, Component contents, int x, int y ) throws IllegalArgumentException {
            while ( !( owner instanceof JDesktopPane ) ) {
                owner = owner.getParent();
                if ( owner == null ) {
                    logger.warning("jME Popup creation failed, default popup created - desktop not found in component hierarchy of "
                                    + owner);
                    return defaultPopupFactory.getPopup( owner, contents, x, y );
                }
            }
            JMEDesktop.LightWeightPopup popup = new JMEDesktop.LightWeightPopup( (JComponent) owner );
            popup.adjust( owner, contents, x, y );
            return popup;
        }
    }

    /**
     * @return current modal component
     * @see #setModalComponent(java.awt.Component)
     */
    public Component getModalComponent() {
        return this.modalComponent;
    }

    /**
     * @see #setModalComponent(java.awt.Component)
     */
    private Component modalComponent;

    /**
     * Filter the swing event to allow events to the specified component and its children only.
     * Note: this does not prevent shortcuts and mnemonics to work for the other components!
     *
     * @param value component that can be exclusively accessed (including children)
     */
    public void setModalComponent( final Component value ) {
        this.modalComponent = value;
    }

    protected void setParent( Node parent ) {
        if ( desktop != null ) {
            super.setParent( parent );
        }
        else {
            throw new IllegalStateException( "already disposed" );
        }
    }

/**
  * Call this method of the desktop is no longer needed. Removes this from the scenegraph, later use is not
  * possible any more.
  */
  public void dispose()
  {
    disposing = true;
    SwingUtilities.invokeLater( new Runnable()
    {
      public void run()
      {
      logger.fine("Disposing JMEDesktop: START");
        doDispose();
        logger.fine("Disposing JMEDesktop: READY");
      }
    } );
  }

  private void doDispose(){
        if ( desktop != null ) {
            if ( getParent() != null ) {
                getParent().detachChild( this );
            }
            if ( rawInputListener != null ) {
                inputManager.removeRawInputListener( rawInputListener );
            }
            desktop.removeAll();
            awtWindow.dispose();
            desktop = null;
            desktopsUsed--;
            if ( desktopsUsed == 0 ) {
                PopupFactory.setSharedInstance( new PopupFactory() );
            }
        }
    }

    private static class ScrollPaneRepaintFixListener implements ContainerListener {
        public void componentAdded( ContainerEvent e ) {
            Component child = e.getChild();
            componentAdded( child );
        }

        private void componentAdded( Component child ) {
            if ( child instanceof Container ) {
                Container container = (Container) child;
                addTo( container );
                container.addContainerListener( this );
            }
            if ( child instanceof JScrollPane ) {
                final JScrollPane scrollPane = (JScrollPane) child;
                // note: the listener added here is only a fix for repaint problems with scrolling
                subscribeRepaintListener( scrollPane.getViewport() );
            }
        }

        private void addTo( Container container ) {
            container.addContainerListener( this );
            for ( int i = 0; i < container.getComponentCount(); i++ ) {
                componentAdded( container.getComponent( i ) );
            }
        }

        private void removeFrom( Container container ) {
            container.removeContainerListener( this );
            for ( int i = 0; i < container.getComponentCount(); i++ ) {
                componentRemoved( container.getComponent( i ) );
            }
        }

        private void subscribeRepaintListener( JViewport viewport ) {
            for ( int i = 0; i < viewport.getChangeListeners().length; i++ ) {
                ChangeListener listener = viewport.getChangeListeners()[i];
                if ( listener instanceof ScrollPaneRepaintChangeListener ) {
                    // listener already subscribed
                    return;
                }
            }
            viewport.addChangeListener( new ScrollPaneRepaintChangeListener( viewport ) );
        }

        public void componentRemoved( ContainerEvent e ) {
            Component child = e.getChild();
            componentRemoved( child );
        }

        private void componentRemoved( Component child ) {
            if ( child instanceof Container ) {
                Container container = (Container) child;
                removeFrom( container );
            }
        }

        private static class ScrollPaneRepaintChangeListener implements ChangeListener {
            private final Component component;

            public ScrollPaneRepaintChangeListener( Component component ) {
                this.component = component;
            }

            public void stateChanged( ChangeEvent e ) {
                component.repaint();
            }
        }
    }

}
TOP

Related Classes of com.jme3.swingGui.JMEDesktop$LightWeightPopup

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.