All Downloads are FREE. Search and download functionalities are using the official Maven repository.

jexer.backend.SwingComponent Maven / Gradle / Ivy

There is a newer version: 1.6.0
Show newest version
/*
 * Jexer - Java Text User Interface
 *
 * The MIT License (MIT)
 *
 * Copyright (C) 2019 Kevin Lamonte
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 *
 * @author Kevin Lamonte [[email protected]]
 * @version 1
 */
package jexer.backend;

import java.awt.Color;
import java.awt.Cursor;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Insets;
import java.awt.Point;
import java.awt.Toolkit;
import java.awt.event.ComponentListener;
import java.awt.event.KeyListener;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.event.MouseWheelListener;
import java.awt.event.WindowListener;
import java.awt.image.BufferedImage;
import java.awt.image.BufferStrategy;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.SwingUtilities;

/**
 * Wrapper for integrating with Swing, because JFrame and JComponent have
 * separate hierarchies.
 */
class SwingComponent {

    // ------------------------------------------------------------------------
    // Variables --------------------------------------------------------------
    // ------------------------------------------------------------------------

    /**
     * If true, use triple buffering when drawing to a JFrame.
     */
    public static boolean tripleBuffer = true;

    /**
     * The frame reference, if we are drawing to a JFrame.
     */
    private JFrame frame;

    /**
     * The component reference, if we are drawing to a JComponent.
     */
    private JComponent component;

    /**
     * An optional border in pixels to add.
     */
    private static final int BORDER = 1;

    /**
     * Adjustable Insets for this component.  This has the effect of adding a
     * black border around the drawing area.
     */
    Insets adjustInsets = new Insets(BORDER + 5, BORDER, BORDER, BORDER);

    // ------------------------------------------------------------------------
    // Constructors -----------------------------------------------------------
    // ------------------------------------------------------------------------

    /**
     * Construct using a JFrame.
     *
     * @param frame the JFrame to draw to
     */
    public SwingComponent(final JFrame frame) {
        this.frame = frame;
        setupFrame();
    }

    /**
     * Construct using a JComponent.
     *
     * @param component the JComponent to draw to
     */
    public SwingComponent(final JComponent component) {
        this.component = component;
        setupComponent();
    }

    // ------------------------------------------------------------------------
    // SwingComponent ---------------------------------------------------------
    // ------------------------------------------------------------------------

    /**
     * Get the BufferStrategy object needed for triple-buffering.
     *
     * @return the BufferStrategy
     * @throws IllegalArgumentException if this function is called when
     * not rendering to a JFrame
     */
    public BufferStrategy getBufferStrategy() {
        if (frame != null) {
            return frame.getBufferStrategy();
        } else {
            throw new IllegalArgumentException("BufferStrategy not used " +
                "for JComponent access");
        }
    }

    /**
     * Get the JFrame reference.
     *
     * @return the frame, or null if this is drawing to a JComponent
     */
    public JFrame getFrame() {
        return frame;
    }

    /**
     * Get the JComponent reference.
     *
     * @return the component, or null if this is drawing to a JFrame
     */
    public JComponent getComponent() {
        return component;
    }

    /**
     * Setup to render to an existing JComponent.
     */
    public void setupComponent() {
        component.setBackground(Color.black);

        if (System.getProperty("jexer.Swing.mouseImage") != null) {
            component.setCursor(getMouseImage());
        } else if (System.getProperty("jexer.Swing.mouseStyle") != null) {
            component.setCursor(getMouseCursor());
        } else if (System.getProperty("jexer.textMouse",
                "true").equals("false")
        ) {
            // If the user has suppressed the text mouse, don't kill the X11
            // mouse.
            component.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
        } else {
            // Kill the X11 cursor
            // Transparent 16 x 16 pixel cursor image.
            BufferedImage cursorImg = new BufferedImage(16, 16,
                BufferedImage.TYPE_INT_ARGB);
            // Create a new blank cursor.
            Cursor blankCursor = Toolkit.getDefaultToolkit().createCustomCursor(
                cursorImg, new Point(0, 0), "blank cursor");
            component.setCursor(blankCursor);
        }

        // Be capable of seeing Tab / Shift-Tab
        component.setFocusTraversalKeysEnabled(false);
    }

    /**
     * Setup to render to an existing JFrame.
     */
    public void setupFrame() {
        frame.setTitle("Jexer Application");
        frame.setBackground(Color.black);
        frame.pack();

        if (System.getProperty("jexer.Swing.mouseImage") != null) {
            frame.setCursor(getMouseImage());
        } else if (System.getProperty("jexer.Swing.mouseStyle") != null) {
            frame.setCursor(getMouseCursor());
        } else if (System.getProperty("jexer.textMouse",
                "true").equals("false")
        ) {
            // If the user has suppressed the text mouse, don't kill the X11
            // mouse.
            frame.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
        } else {
            // Kill the X11 cursor
            // Transparent 16 x 16 pixel cursor image.
            BufferedImage cursorImg = new BufferedImage(16, 16,
                BufferedImage.TYPE_INT_ARGB);
            // Create a new blank cursor.
            Cursor blankCursor = Toolkit.getDefaultToolkit().createCustomCursor(
                cursorImg, new Point(0, 0), "blank cursor");
            frame.setCursor(blankCursor);
        }

        // Be capable of seeing Tab / Shift-Tab
        frame.setFocusTraversalKeysEnabled(false);

        // Setup triple-buffering
        if (tripleBuffer) {
            frame.setIgnoreRepaint(true);
            frame.createBufferStrategy(3);
        }
    }

    /**
     * Load an image named in jexer.Swing.mouseImage as the mouse cursor.
     * The image must be on the classpath.
     *
     * @return the cursor
     */
    private Cursor getMouseImage() {
        Cursor cursor = Cursor.getDefaultCursor();
        String filename = System.getProperty("jexer.Swing.mouseImage");
        assert (filename != null);

        try {
            ClassLoader loader = Thread.currentThread().
                getContextClassLoader();

            java.net.URL url = loader.getResource(filename);
            if (url == null) {
                // User named a file, but it's not on the classpath.  Bail
                // out.
                return cursor;
            }

            BufferedImage cursorImage = ImageIO.read(url);
            java.awt.Dimension cursorSize = Toolkit.getDefaultToolkit().
                getBestCursorSize(
                        cursorImage.getWidth(), cursorImage.getHeight());

            cursor = Toolkit.getDefaultToolkit().createCustomCursor(cursorImage,
                new Point((int) Math.min(cursorImage.getWidth() / 2,
                        cursorSize.getWidth() - 1),
                    (int) Math.min(cursorImage.getHeight() / 2,
                        cursorSize.getHeight() - 1)),
                "custom cursor");
        } catch (IOException e) {
            e.printStackTrace();
        }

        return cursor;
    }

    /**
     * Get the appropriate mouse cursor based on jexer.Swing.mouseStyle.
     *
     * @return the cursor
     */
    private Cursor getMouseCursor() {
        Cursor cursor = Cursor.getDefaultCursor();
        String style = System.getProperty("jexer.Swing.mouseStyle");
        assert (style != null);

        style = style.toLowerCase();

        if (style.equals("none")) {
            // Transparent 16 x 16 pixel cursor image.
            BufferedImage cursorImg = new BufferedImage(16, 16,
                BufferedImage.TYPE_INT_ARGB);
            // Create a new blank cursor.
            cursor = Toolkit.getDefaultToolkit().createCustomCursor(
                cursorImg, new Point(0, 0), "blank cursor");
        } else if (style.equals("default")) {
            cursor = Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR);
        } else if (style.equals("hand")) {
            cursor = Cursor.getPredefinedCursor(Cursor.HAND_CURSOR);
        } else if (style.equals("text")) {
            cursor = Cursor.getPredefinedCursor(Cursor.TEXT_CURSOR);
        } else if (style.equals("move")) {
            cursor = Cursor.getPredefinedCursor(Cursor.MOVE_CURSOR);
        } else if (style.equals("crosshair")) {
            cursor = Cursor.getPredefinedCursor(Cursor.CROSSHAIR_CURSOR);
        }

        return cursor;
    }

    /**
     * Set the window title.
     *
     * @param title the new title
     */
    public void setTitle(final String title) {
        if (frame != null) {
            frame.setTitle(title);
        }
    }

    /**
     * Paints this component.
     *
     * @param g the graphics context to use for painting
     */
    public void paint(Graphics g) {
        if (frame != null) {
            frame.paint(g);
        } else {
            component.paint(g);
        }
    }

    /**
     * Repaints this component.
     */
    public void repaint() {
        if (frame != null) {
            frame.repaint();
        } else {
            component.repaint();
        }
    }

    /**
     * Repaints the specified rectangle of this component.
     *
     * @param x the x coordinate
     * @param y the y coordinate
     * @param width the width
     * @param height the height
     */
    public void repaint(int x, int y, int width, int height) {
        if (frame != null) {
            frame.repaint(x, y, width, height);
        } else {
            component.repaint(x, y, width, height);
        }
    }

    /**
     * If a border has been set on this component, returns the border's
     * insets; otherwise calls super.getInsets.
     *
     * @return the value of the insets property
     */
    public Insets getInsets() {
        Insets swingInsets = null;
        if (frame != null) {
            swingInsets = frame.getInsets();
        } else {
            swingInsets = component.getInsets();
        }
        Insets result = new Insets(swingInsets.top + adjustInsets.top,
            swingInsets.left + adjustInsets.left,
            swingInsets.bottom + adjustInsets.bottom,
            swingInsets.right + adjustInsets.right);
        return result;
    }

    /**
     * Returns the current width of this component.
     *
     * @return the current width of this component
     */
    public int getWidth() {
        if (frame != null) {
            return frame.getWidth();
        } else {
            return component.getWidth();
        }
    }

    /**
     * Returns the current height of this component.
     *
     * @return the current height of this component
     */
    public int getHeight() {
        if (frame != null) {
            return frame.getHeight();
        } else {
            return component.getHeight();
        }
    }

    /**
     * Gets the font of this component.
     *
     * @return this component's font; if a font has not been set for this
     * component, the font of its parent is returned
     */
    public Font getFont() {
        if (frame != null) {
            return frame.getFont();
        } else {
            return component.getFont();
        }
    }

    /**
     * Sets the font of this component.
     *
     * @param f the font to become this component's font; if this parameter
     * is null then this component will inherit the font of its parent
     */
    public void setFont(final Font f) {
        if (frame != null) {
            frame.setFont(f);
        } else {
            component.setFont(f);
        }
    }

    /**
     * Shows or hides this Window depending on the value of parameter b.
     *
     * @param b if true, make visible, else make invisible
     */
    public void setVisible(final boolean b) {
        if (frame != null) {
            frame.setVisible(b);
        } else {
            component.setVisible(b);
        }
    }

    /**
     * Creates a graphics context for this component. This method will return
     * null if this component is currently not displayable.
     *
     * @return a graphics context for this component, or null if it has none
     */
    public Graphics getGraphics() {
        if (frame != null) {
            return frame.getGraphics();
        } else {
            return component.getGraphics();
        }
    }

    /**
     * Releases all of the native screen resources used by this Window, its
     * subcomponents, and all of its owned children. That is, the resources
     * for these Components will be destroyed, any memory they consume will
     * be returned to the OS, and they will be marked as undisplayable.
     */
    public void dispose() {
        if (frame != null) {
            frame.dispose();
        } else {
            component.getParent().remove(component);
        }
    }

    /**
     * Resize the component to match the font dimensions.
     *
     * @param width the new width in pixels
     * @param height the new height in pixels
     */
    public void setDimensions(final int width, final int height) {
        if (SwingUtilities.isEventDispatchThread()) {
            // We are in the Swing thread and can safely set the size.

            // Figure out the thickness of borders and use that to set the
            // final size.
            if (frame != null) {
                Insets insets = getInsets();
                frame.setSize(width + insets.left + insets.right,
                    height + insets.top + insets.bottom);
            } else {
                Insets insets = getInsets();
                component.setSize(width + insets.left + insets.right,
                    height + insets.top + insets.bottom);
            }
            return;
        }

        SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                // Figure out the thickness of borders and use that to set
                // the final size.
                if (frame != null) {
                    Insets insets = getInsets();
                    frame.setSize(width + insets.left + insets.right,
                        height + insets.top + insets.bottom);
                } else {
                    Insets insets = getInsets();
                    component.setSize(width + insets.left + insets.right,
                        height + insets.top + insets.bottom);
                }
            }
        });
    }

    /**
     * Adds the specified component listener to receive component events from
     * this component. If listener l is null, no exception is thrown and no
     * action is performed.
     *
     * @param l the component listener
     */
    public void addComponentListener(ComponentListener l) {
        if (frame != null) {
            frame.addComponentListener(l);
        } else {
            component.addComponentListener(l);
        }
    }

    /**
     * Adds the specified key listener to receive key events from this
     * component. If l is null, no exception is thrown and no action is
     * performed.
     *
     * @param l the key listener.
     */
    public void addKeyListener(KeyListener l) {
        if (frame != null) {
            frame.addKeyListener(l);
        } else {
            component.addKeyListener(l);
        }
    }

    /**
     * Adds the specified mouse listener to receive mouse events from this
     * component. If listener l is null, no exception is thrown and no action
     * is performed.
     *
     * @param l the mouse listener
     */
    public void addMouseListener(MouseListener l) {
        if (frame != null) {
            frame.addMouseListener(l);
        } else {
            component.addMouseListener(l);
        }
    }

    /**
     * Adds the specified mouse motion listener to receive mouse motion
     * events from this component. If listener l is null, no exception is
     * thrown and no action is performed.
     *
     * @param l the mouse motion listener
     */
    public void addMouseMotionListener(MouseMotionListener l) {
        if (frame != null) {
            frame.addMouseMotionListener(l);
        } else {
            component.addMouseMotionListener(l);
        }
    }

    /**
     * Adds the specified mouse wheel listener to receive mouse wheel events
     * from this component. Containers also receive mouse wheel events from
     * sub-components.
     *
     * @param l the mouse wheel listener
     */
    public void addMouseWheelListener(MouseWheelListener l) {
        if (frame != null) {
            frame.addMouseWheelListener(l);
        } else {
            component.addMouseWheelListener(l);
        }
    }

    /**
     * Adds the specified window listener to receive window events from this
     * window. If l is null, no exception is thrown and no action is
     * performed.
     *
     * @param l the window listener
     */
    public void addWindowListener(WindowListener l) {
        if (frame != null) {
            frame.addWindowListener(l);
        }
    }

    /**
     * Requests that this Component get the input focus, if this Component's
     * top-level ancestor is already the focused Window.
     */
    public void requestFocusInWindow() {
        if (frame != null) {
            frame.requestFocusInWindow();
        } else {
            component.requestFocusInWindow();
        }
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy