jna-master/contrib/shapedwindowdemo/com/sun/jna/contrib/demo/ShapedWindowDemo.java
/* Copyright (c) 2007 Timothy Wall, All Rights Reserved
 *
 * The contents of this file is dual-licensed under 2
 * alternative Open Source/Free licenses: LGPL 2.1 or later and
 * Apache License 2.0. (starting with JNA version 4.0.0).
 *
 * You can freely decide which license you want to apply to
 * the project.
 *
 * You may obtain a copy of the LGPL License at:
 *
 * http://www.gnu.org/licenses/licenses.html
 *
 * A copy is also included in the downloadable source code package
 * containing JNA, in file "LGPL2.1".
 *
 * You may obtain a copy of the Apache License at:
 *
 * http://www.apache.org/licenses/
 *
 * A copy is also included in the downloadable source code package
 * containing JNA, in file "AL2.0".
 */
package com.sun.jna.contrib.demo;

import java.awt.AlphaComposite;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Composite;
import java.awt.Cursor;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Frame;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.Point;
import java.awt.RenderingHints;
import java.awt.Shape;
import java.awt.Stroke;
import java.awt.Window;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.geom.Area;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
import java.util.Calendar;

import javax.swing.AbstractAction;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JPopupMenu;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.event.MouseInputAdapter;

import com.sun.jna.platform.WindowUtils;

/** Example which uses the {@link WindowUtils} class.  Demonstrates the
 * definition of a cross-platform library with several platform-specific
 * implementations based on JNA native library definitions.
 */
public class ShapedWindowDemo {

    public static final int ICON_SIZE = 64;

    private static class ClockFace extends JComponent {
        private static final long serialVersionUID = 1L;
        private Stroke border;
        private Stroke secondHand;
        private Stroke minuteHand;
        private Stroke hourHand;
        private Stroke ticks;
        public ClockFace(Dimension size) {
            setPreferredSize(size);
            setSize(size);
            setOpaque(false);
            Timer timer = new Timer(1000, new ActionListener() {
                public void actionPerformed(ActionEvent e) {
                    repaint();
                    Window w = SwingUtilities.getWindowAncestor(ClockFace.this);
                    while (!(w instanceof Frame)) {
                        w = w.getOwner();
                    }
                    if (w instanceof Frame) {
                        ((Frame)w).setIconImage(getIconImage());
                    }
                }
            });
            timer.setRepeats(true);
            timer.start();
        }
        private String getRomanNumeral(int number) {
            switch(number) {
                case 1: return "I";
                case 2: return "II";
                case 3: return "III";
                case 4: return "IV";
                case 5: return "V";
                case 6: return "VI";
                case 7: return "VII";
                case 8: return "VIII";
                case 9: return "IX";
                case 10: return "X";
                case 11: return "XI";
                case 12:
                default:
                    return "XII";
            }
        }
        protected void paintComponent(Graphics graphics) {
            paintFace(graphics, Math.min(getWidth(), getHeight()));
        }

        protected void paintFace(Graphics graphics, int size) {
            Point center = new Point(size/2, size/2);
            int radius = center.x;
            int margin = radius / 20;

            int w = size;
            border = new BasicStroke(Math.max(1f, w/150f), BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND);
            secondHand = new BasicStroke(Math.max(1f, w/75f), BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND);
            minuteHand = new BasicStroke(Math.max(1f, w/38f), BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND);
            hourHand = new BasicStroke(Math.max(1.5f, w/20f), BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND);
            ticks = new BasicStroke(1f);

            Graphics2D g = (Graphics2D)graphics.create();
            g.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
                               RenderingHints.VALUE_ANTIALIAS_ON);
            g.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING,
                               RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
            g.setRenderingHint(RenderingHints.KEY_RENDERING,
                               RenderingHints.VALUE_RENDER_QUALITY);
            Color bg = getBackground();
            g.setColor(new Color(bg.getRed(), bg.getGreen(), bg.getBlue()));

            g.fill(new Ellipse2D.Float(0,0,size,size));
            Font font = getFont();
            g.setFont(font.deriveFont(Font.BOLD, size/12));
            g.setColor(new Color(0,0,0,128));
            g.setStroke(border);
            g.draw(new Ellipse2D.Float(0,0,size-1,size-1));
            g.draw(new Ellipse2D.Float(margin,margin,size-margin*2-1,size-margin*2-1));

            Calendar c = Calendar.getInstance();
            int minute = c.get(Calendar.MINUTE);
            int hour = c.get(Calendar.HOUR);
            int second = c.get(Calendar.SECOND);
            g.translate(center.x, center.y);
            g.setColor(getForeground());
            int numbers = radius * 3 / 4;
            for (int i=0;i < 12;i++) {
                double theta = Math.PI*2*i/12;
                String str = getRomanNumeral((i+2)%12+1);
                Rectangle2D rect = g.getFontMetrics().getStringBounds(str, g);
                g.drawString(str, Math.round(numbers*Math.cos(theta)-rect.getWidth()/2),
                             Math.round(numbers*Math.sin(theta)+margin*2));
            }
            for (int i=0;i < 60;i++) {
                g.setColor(getForeground());
                g.setStroke(ticks);
                g.drawLine(radius-margin*2, 0, radius-margin, 0);
                if ((i % 5) == 0) {
                    g.drawLine(radius-margin*3, 0, radius-margin, 0);
                }
                if ((i + 15) % 60 == minute) {
                    g.setStroke(minuteHand);
                    g.drawLine(0, 0, radius-margin*4, 0);
                }
                if ((i + 15) % 60 == (hour * 5 + minute * 5 / 60)) {
                    g.setStroke(hourHand);
                    g.drawLine(0, 0, radius/2, 0);
                }
                if ((i + 15) % 60 == second) {
                    g.setColor(new Color(255, 0, 0, 128));
                    g.setStroke(secondHand);
                    g.drawLine(0, 0, radius-margin*4, 0);
                }
                g.rotate(Math.PI*2/60);
            }
            g.dispose();
        }

        public Image getIconImage() {
            BufferedImage image = new BufferedImage(ICON_SIZE, ICON_SIZE,
                                                    BufferedImage.TYPE_INT_ARGB);
            Graphics2D g = image.createGraphics();
            g.setRenderingHint(RenderingHints.KEY_RENDERING,
                               RenderingHints.VALUE_RENDER_QUALITY);
            Composite old = g.getComposite();
            g.setComposite(AlphaComposite.Clear);
            g.fillRect(0, 0, image.getWidth(), image.getHeight());
            g.setComposite(old);
            paintFace(g, ICON_SIZE);
            return image;
        }
    }

    public static void main(String[] args) {
        try {
            System.setProperty("sun.java2d.noddraw", "true");
            UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
        }
        catch(Exception e) {
        }
        final JFrame frame = new JFrame("Shaped Window Demo");
        MouseInputAdapter handler = new MouseInputAdapter() {
            private Point offset;
            private void showPopup(MouseEvent e) {
                final JPopupMenu m = new JPopupMenu();
                m.add(new AbstractAction("Hide") {
                    public void actionPerformed(ActionEvent e) {
                        frame.setState(JFrame.ICONIFIED);
                    }
                    private static final long serialVersionUID = 1L;
                });
                m.add(new AbstractAction("Close") {
                    public void actionPerformed(ActionEvent e) {
                        System.exit(0);
                    }
                    private static final long serialVersionUID = 1L;
                });
                m.pack();
                m.show(e.getComponent(), e.getX(), e.getY());
            }
            public void mousePressed(MouseEvent e) {
                offset = e.getPoint();
                if (e.isPopupTrigger()) {
                    showPopup(e);
                }
            }
            public void mouseDragged(MouseEvent e) {
                if (!SwingUtilities.isLeftMouseButton(e))
                    return;
                Point where = e.getPoint();
                where.translate(-offset.x, -offset.y);
                Point loc = frame.getLocationOnScreen();
                loc.translate(where.x, where.y);
                frame.setLocation(loc.x, loc.y);
            }
            public void mouseReleased(MouseEvent e) {
                if (e.isPopupTrigger()) {
                    showPopup(e);
                }
            }
        };
        frame.addMouseListener(handler);
        frame.addMouseMotionListener(handler);
        ClockFace face = new ClockFace(new Dimension(150, 150));
        frame.getContentPane().setCursor(Cursor.getPredefinedCursor(Cursor.MOVE_CURSOR));
        frame.getContentPane().add(face);
        frame.setUndecorated(true);
        try {
            Shape mask = new Area(new Ellipse2D.Float(0, 0, 150, 150));
            WindowUtils.setWindowMask(frame, mask);
            if (WindowUtils.isWindowAlphaSupported()) {
                WindowUtils.setWindowAlpha(frame, .7f);
            }
            frame.setIconImage(face.getIconImage());
            frame.setResizable(false);
            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

            frame.pack();
            frame.setLocation(100, 100);
            frame.setVisible(true);
        }
        catch(UnsatisfiedLinkError e) {
            e.printStackTrace();
            String msg = e.getMessage()
                + "\nError loading the JNA library";
            JTextArea area = new JTextArea(msg);
            area.setOpaque(false);
            area.setFont(UIManager.getFont("Label.font"));
            area.setEditable(false);
            area.setColumns(80);
            area.setRows(8);
            area.setWrapStyleWord(true);
            area.setLineWrap(true);
            JOptionPane.showMessageDialog(frame, new JScrollPane(area),
                                          "Library Load Error: "
                                          + System.getProperty("os.name")
                                          + "/" + System.getProperty("os.arch"),
                                          JOptionPane.ERROR_MESSAGE);
            System.exit(1);
        }
    }
}
