Merge commit 'e6bb1700749980e69b5e913acbfd276f129c24dc'
[nikiroo-utils.git] / src / jexer / backend / SwingComponent.java
index 92fd1d897afdb1679fd40f6a15394c09d93fdcce..df3633398b699585757cab519b1102214983a50c 100644 (file)
@@ -43,8 +43,11 @@ 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
@@ -80,7 +83,7 @@ class SwingComponent {
      * 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);
+    Insets adjustInsets = null;
 
     // ------------------------------------------------------------------------
     // Constructors -----------------------------------------------------------
@@ -93,6 +96,16 @@ class SwingComponent {
      */
     public SwingComponent(final JFrame frame) {
         this.frame = frame;
+        if (System.getProperty("os.name").startsWith("Linux")) {
+            // On my Linux dev system, a Swing frame draws its contents just
+            // a little off.  No idea why, but I've seen it on both Debian
+            // and Fedora with KDE.  These adjustments to the adjustments
+            // seem to center it OK in the frame.
+            adjustInsets = new Insets(BORDER + 5, BORDER,
+                BORDER - 3, BORDER + 2);
+        } else {
+            adjustInsets = new Insets(BORDER, BORDER, BORDER, BORDER);
+        }
         setupFrame();
     }
 
@@ -103,6 +116,7 @@ class SwingComponent {
      */
     public SwingComponent(final JComponent component) {
         this.component = component;
+        adjustInsets = new Insets(BORDER, BORDER, BORDER, BORDER);
         setupComponent();
     }
 
@@ -150,14 +164,26 @@ class SwingComponent {
     public void setupComponent() {
         component.setBackground(Color.black);
 
-        // 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);
+        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);
@@ -171,14 +197,26 @@ class SwingComponent {
         frame.setBackground(Color.black);
         frame.pack();
 
-        // 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);
+        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);
@@ -190,6 +228,80 @@ class SwingComponent {
         }
     }
 
+    /**
+     * 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.
      *
@@ -363,17 +475,38 @@ class SwingComponent {
      * @param height the new height in pixels
      */
     public void setDimensions(final int width, final int height) {
-        // 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);
+        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);
+                }
+            }
+        });
     }
 
     /**
@@ -464,4 +597,16 @@ class SwingComponent {
         }
     }
 
+    /**
+     * 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();
+        }
+    }
+
 }