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         // To avoid popup cutting by main window shape forbid light-weight popups
 139         jPopupMenu.setLightWeightPopupEnabled(false);
 140     }
 141 
 142     /**
 143      * Implements mouse-related behavior: window dragging and popup menu
 144      * invocation
 145      */
 146     private final MouseAdapter mouseListener = new MouseAdapter() {
 147 
 148         int x, y;
 149 
 150         @Override
 151         public void mousePressed(MouseEvent e) {
 152             if (e.getButton() == MouseEvent.BUTTON1) {
 153                 x = e.getX();
 154                 y = e.getY();
 155             }
 156         }
 157 
 158         @Override
 159         public void mouseDragged(MouseEvent e) {
 160             if ((e.getModifiersEx() & MouseEvent.BUTTON1_DOWN_MASK) != 0) {
 161                 setLocation(e.getXOnScreen() - x, e.getYOnScreen() - y);
 162             }
 163         }
 164 
 165         @Override
 166         public void mouseReleased(MouseEvent e) {
 167             if (e.isPopupTrigger()) {
 168                 jPopupMenu.show(getContentPane(), e.getX(), e.getY());
 169             }
 170         }
 171     };
 172 
 173     /**
 174      * Implements keyboard navigation. Arrows move by 5 pixels, Ctrl + arrows
 175      * move by 50 pixels, Alt + arrows move by 1 pixel.
 176      * Esc exits the application.
 177      */
 178     private final KeyAdapter keyboardListener = new KeyAdapter() {
 179 
 180         @Override
 181         public void keyPressed(KeyEvent e) {
 182             int step = e.isControlDown() ? 50 : e.isAltDown() ? 1 : 5;
 183             switch (e.getKeyCode()) {
 184                 case KeyEvent.VK_LEFT:
 185                     setLocation(getX() - step, getY());
 186                     break;
 187                 case KeyEvent.VK_RIGHT:
 188                     setLocation(getX() + step, getY());
 189                     break;
 190                 case KeyEvent.VK_UP:
 191                     setLocation(getX(), getY() - step);
 192                     break;
 193                 case KeyEvent.VK_DOWN:
 194                     setLocation(getX(), getY() + step);
 195                     break;
 196                 case KeyEvent.VK_ESCAPE:
 197                     exitAction.actionPerformed(null);
 198             }
 199         }
 200     };
 201 
 202     public Ruler() {
 203         setUndecorated(true);
 204 
 205         // Enables perpixel translucency
 206         setBackground(new Color(BACKGROUND.getRed(), BACKGROUND.getGreen(),
 207                 BACKGROUND.getBlue(), OPACITY));
 208 
 209         addMouseListener(mouseListener);
 210         addMouseMotionListener(mouseListener);
 211         addComponentListener(componentListener);
 212         addKeyListener(keyboardListener);
 213         setContentPane(new JPanel() {
 214 
 215             @Override
 216             protected void paintComponent(Graphics g) {
 217                 Graphics2D gg = (Graphics2D) g.create();
 218                 int w = getWidth();
 219                 int h = getHeight();
 220                 int hh = gg.getFontMetrics().getAscent();
 221 
 222                 // This is an approach to apply shape when PERPIXEL_TRANSPARENT
 223                 // isn't supported
 224                 if (!transparencySupported) {
 225                     gg.setBackground(new Color(0, 0, 0, 0));
 226                     gg.clearRect(0, 0, w, h);
 227                     gg.clip(buildShape());
 228 
 229                     gg.setBackground(Ruler.this.getBackground());
 230                     gg.clearRect(0, 0, w, h);
 231                 }
 232 
 233                 gg.setColor(FOREGROUND);
 234                 for (int x = 0; x < w * (h - 8) / h - 5; x += 5) {
 235                     boolean hi = x % 50 == 0;
 236                     gg.drawLine(x + 5, 0, x + 5,
 237                             hi ? 20 : (x % 25 == 0 ? 13 : 8));
 238                     if (hi) {
 239                         String number = Integer.toString(x);
 240                         int ww = gg.getFontMetrics().stringWidth(number);
 241                         gg.drawString(number, x + 5 - ww / 2, 20 + hh);
 242                     }
 243                 }
 244 
 245                 gg.dispose();
 246             }
 247         });
 248         setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
 249         setSize(F_WIDTH, F_HEIGHT);
 250         setLocationByPlatform(true);
 251     }
 252 
 253     /**
 254      * @param args the command line arguments are ignored
 255      */
 256     public static void main(String[] args) throws InterruptedException, InvocationTargetException {
 257 
 258         SwingUtilities.invokeAndWait(new Runnable() {
 259 
 260             @Override
 261             public void run() {
 262                 translucencySupported = checkTranslucencyMode(PERPIXEL_TRANSLUCENT);
 263                 transparencySupported = checkTranslucencyMode(PERPIXEL_TRANSPARENT);
 264 
 265                 if (!translucencySupported) {
 266                     System.err.println("This application requires "
 267                             + "'PERPIXEL_TRANSLUCENT' translucency mode to "
 268                             + "be supported.");
 269                     System.exit(-1);
 270                 }
 271 
 272                 Ruler ruler = new Ruler();
 273                 ruler.setVisible(true);
 274             }
 275         });
 276     }
 277 }