1 /*
   2  * Copyright (c) 2011, 2012, Oracle and/or its affiliates. All rights reserved.
   3  *
   4  * Redistribution and use in source and binary forms, with or without
   5  * modification, are permitted provided that the following conditions
   6  * are met:
   7  *
   8  *   - Redistributions of source code must retain the above copyright
   9  *     notice, this list of conditions and the following disclaimer.
  10  *
  11  *   - Redistributions in binary form must reproduce the above copyright
  12  *     notice, this list of conditions and the following disclaimer in the
  13  *     documentation and/or other materials provided with the distribution.
  14  *
  15  *   - Neither the name of Oracle nor the names of its
  16  *     contributors may be used to endorse or promote products derived
  17  *     from this software without specific prior written permission.
  18  *
  19  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
  20  * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
  21  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
  22  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR
  23  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
  24  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
  25  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
  26  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
  27  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
  28  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  29  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  30  */
  31 
  32 /*
  33  * This source code is provided to illustrate the usage of a given feature
  34  * or technique and has been deliberately simplified. Additional steps
  35  * required for a production-quality application, such as security checks,
  36  * input validation and proper error handling, might not be present in
  37  * this sample code.
  38  */
  39 
  40 package transparentruler;
  41 
  42 
  43 import java.awt.*;
  44 import java.awt.GraphicsDevice.WindowTranslucency;
  45 import static java.awt.GraphicsDevice.WindowTranslucency.*;
  46 import java.awt.event.ActionEvent;
  47 import java.awt.event.ComponentAdapter;
  48 import java.awt.event.ComponentEvent;
  49 import java.awt.event.KeyAdapter;
  50 import java.awt.event.KeyEvent;
  51 import java.awt.event.MouseAdapter;
  52 import java.awt.event.MouseEvent;
  53 import java.awt.geom.Path2D.Float;
  54 import java.lang.reflect.InvocationTargetException;
  55 import javax.swing.AbstractAction;
  56 import javax.swing.Action;
  57 import javax.swing.JFrame;
  58 import javax.swing.JMenuItem;
  59 import javax.swing.JPanel;
  60 import javax.swing.JPopupMenu;
  61 import javax.swing.SwingUtilities;
  62 import javax.swing.WindowConstants;
  63 
  64 
  65 /**
  66  * This sample demonstrates shaped and translucent window feature.
  67  * @author Alexander Kouznetsov
  68  */
  69 @SuppressWarnings("serial")
  70 public class Ruler extends JFrame {
  71 
  72     private static final Color BACKGROUND = Color.RED;
  73     private static final Color FOREGROUND = Color.WHITE;
  74     private static final int OPACITY = 180;
  75     private static final int W = 70;
  76     private static final int F_HEIGHT = 400;
  77     private static final int F_WIDTH = (int) (F_HEIGHT * 1.618 + 0.5);
  78 
  79     private static boolean translucencySupported;
  80     private static boolean transparencySupported;
  81 
  82     private static boolean checkTranslucencyMode(WindowTranslucency arg) {
  83         GraphicsEnvironment ge =
  84                 GraphicsEnvironment.getLocalGraphicsEnvironment();
  85         GraphicsDevice gd = ge.getDefaultScreenDevice();
  86         return gd.isWindowTranslucencySupported(arg);
  87     }
  88 
  89     public Shape buildShape() {
  90         int h = getHeight();
  91         int w = getWidth();
  92         float a = (float) Math.hypot(h, w);
  93         Float path = new java.awt.geom.Path2D.Float();
  94         path.moveTo(0, 0);
  95         path.lineTo(w, 0);
  96         path.lineTo(0, h);
  97         path.closePath();
  98         path.moveTo(W, W);
  99         path.lineTo(W, h - W * (a + h) / w);
 100         path.lineTo(w - W * (a + w) / h, W);
 101         path.closePath();
 102         return path;
 103     }
 104 
 105     private final ComponentAdapter componentListener = new ComponentAdapter() {
 106 
 107         /**
 108          * Applies the shape to window. It is recommended to apply shape in
 109          * componentResized() method
 110          */
 111         @Override
 112         public void componentResized(ComponentEvent e) {
 113 
 114             // We do apply shape only if PERPIXEL_TRANSPARENT is supported
 115             if (transparencySupported) {
 116                 setShape(buildShape());
 117             }
 118         }
 119     };
 120 
 121     private final Action exitAction = new AbstractAction("Exit") {
 122 
 123         {
 124             putValue(Action.MNEMONIC_KEY, KeyEvent.VK_X);
 125         }
 126 
 127         @Override
 128         public void actionPerformed(ActionEvent e) {
 129             System.exit(0);
 130         }
 131     };
 132 
 133     private final JPopupMenu jPopupMenu = new JPopupMenu();
 134 
 135     {
 136         jPopupMenu.add(new JMenuItem(exitAction));
 137     }
 138 
 139     /**
 140      * Implements mouse-related behavior: window dragging and popup menu
 141      * invocation
 142      */
 143     private final MouseAdapter mouseListener = new MouseAdapter() {
 144 
 145         int x, y;
 146 
 147         @Override
 148         public void mousePressed(MouseEvent e) {
 149             if (e.getButton() == MouseEvent.BUTTON1) {
 150                 x = e.getX();
 151                 y = e.getY();
 152             }
 153         }
 154 
 155         @Override
 156         public void mouseDragged(MouseEvent e) {
 157             if ((e.getModifiersEx() & MouseEvent.BUTTON1_DOWN_MASK) != 0) {
 158                 setLocation(e.getXOnScreen() - x, e.getYOnScreen() - y);
 159             }
 160         }
 161 
 162         @Override
 163         public void mouseReleased(MouseEvent e) {
 164             if (e.isPopupTrigger()) {
 165                 jPopupMenu.show(getContentPane(), e.getX(), e.getY());
 166             }
 167         }
 168     };
 169 
 170     /**
 171      * Implements keyboard navigation. Arrows move by 5 pixels, Ctrl + arrows
 172      * move by 50 pixels, Alt + arrows move by 1 pixel.
 173      * Esc exits the application.
 174      */
 175     private final KeyAdapter keyboardListener = new KeyAdapter() {
 176 
 177         @Override
 178         public void keyPressed(KeyEvent e) {
 179             int step = e.isControlDown() ? 50 : e.isAltDown() ? 1 : 5;
 180             switch (e.getKeyCode()) {
 181                 case KeyEvent.VK_LEFT:
 182                     setLocation(getX() - step, getY());
 183                     break;
 184                 case KeyEvent.VK_RIGHT:
 185                     setLocation(getX() + step, getY());
 186                     break;
 187                 case KeyEvent.VK_UP:
 188                     setLocation(getX(), getY() - step);
 189                     break;
 190                 case KeyEvent.VK_DOWN:
 191                     setLocation(getX(), getY() + step);
 192                     break;
 193                 case KeyEvent.VK_ESCAPE:
 194                     exitAction.actionPerformed(null);
 195             }
 196         }
 197     };
 198 
 199     public Ruler() {
 200         setUndecorated(true);
 201 
 202         // Enables perpixel translucency
 203         setBackground(new Color(BACKGROUND.getRed(), BACKGROUND.getGreen(),
 204                 BACKGROUND.getBlue(), OPACITY));
 205 
 206         addMouseListener(mouseListener);
 207         addMouseMotionListener(mouseListener);
 208         addComponentListener(componentListener);
 209         addKeyListener(keyboardListener);
 210         setContentPane(new JPanel() {
 211 
 212             @Override
 213             protected void paintComponent(Graphics g) {
 214                 Graphics2D gg = (Graphics2D) g.create();
 215                 int w = getWidth();
 216                 int h = getHeight();
 217                 int hh = gg.getFontMetrics().getAscent();
 218 
 219                 // This is an approach to apply shape when PERPIXEL_TRANSPARENT
 220                 // isn't supported
 221                 if (!transparencySupported) {
 222                     gg.setBackground(new Color(0, 0, 0, 0));
 223                     gg.clearRect(0, 0, w, h);
 224                     gg.clip(buildShape());
 225 
 226                     gg.setBackground(Ruler.this.getBackground());
 227                     gg.clearRect(0, 0, w, h);
 228                 }
 229 
 230                 gg.setColor(FOREGROUND);
 231                 for (int x = 0; x < w * (h - 8) / h - 5; x += 5) {
 232                     boolean hi = x % 50 == 0;
 233                     gg.drawLine(x + 5, 0, x + 5,
 234                             hi ? 20 : (x % 25 == 0 ? 13 : 8));
 235                     if (hi) {
 236                         String number = Integer.toString(x);
 237                         int ww = gg.getFontMetrics().stringWidth(number);
 238                         gg.drawString(number, x + 5 - ww / 2, 20 + hh);
 239                     }
 240                 }
 241 
 242                 gg.dispose();
 243             }
 244         });
 245         setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
 246         setSize(F_WIDTH, F_HEIGHT);
 247         setLocationByPlatform(true);
 248     }
 249 
 250     /**
 251      * @param args the command line arguments are ignored
 252      */
 253     public static void main(String[] args) throws InterruptedException, InvocationTargetException {
 254 
 255         SwingUtilities.invokeAndWait(new Runnable() {
 256 
 257             @Override
 258             public void run() {
 259                 translucencySupported = checkTranslucencyMode(PERPIXEL_TRANSLUCENT);
 260                 transparencySupported = checkTranslucencyMode(PERPIXEL_TRANSPARENT);
 261 
 262                 if (!translucencySupported) {
 263                     System.err.println("This application requires "
 264                             + "'PERPIXEL_TRANSLUCENT' translucency mode to "
 265                             + "be supported.");
 266                     System.exit(-1);
 267                 }
 268 
 269                 Ruler ruler = new Ruler();
 270                 ruler.setVisible(true);
 271             }
 272         });
 273     }
 274 }