< prev index next >

src/java.desktop/macosx/classes/sun/lwawt/macosx/CTrayIcon.java

Print this page


   1 /*
   2  * Copyright (c) 2011, 2016, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package sun.lwawt.macosx;
  27 
  28 import sun.awt.AWTAccessor;
  29 import sun.awt.SunToolkit;
  30 
  31 import javax.swing.*;
  32 import java.awt.*;
  33 import java.awt.event.*;










  34 import java.awt.geom.Point2D;
  35 import java.awt.image.BufferedImage;
  36 import java.awt.peer.TrayIconPeer;
  37 import java.beans.PropertyChangeEvent;
  38 import java.beans.PropertyChangeListener;
  39 import java.util.concurrent.atomic.AtomicReference;
  40 
  41 import static sun.awt.AWTAccessor.*;






  42 
  43 public class CTrayIcon extends CFRetainedResource implements TrayIconPeer {
  44     private TrayIcon target;
  45     private PopupMenu popup;
  46     private JDialog messageDialog;
  47     private DialogEventHandler handler;
  48 
  49     // In order to construct MouseEvent object, we need to specify a
  50     // Component target. Because TrayIcon isn't Component's subclass,
  51     // we use this dummy frame instead
  52     private final Frame dummyFrame;
  53 
  54     // A bitmask that indicates what mouse buttons produce MOUSE_CLICKED events
  55     // on MOUSE_RELEASE. Click events are only generated if there were no drag
  56     // events between MOUSE_PRESSED and MOUSE_RELEASED for particular button
  57     private static int mouseClickButtons = 0;
  58 
  59     CTrayIcon(TrayIcon target) {
  60         super(0, true);
  61 
  62         this.messageDialog = null;
  63         this.handler = null;
  64         this.target = target;
  65         this.popup = target.getPopupMenu();
  66         this.dummyFrame = new Frame();
  67         setPtr(createModel());
  68 
  69         //if no one else is creating the peer.
  70         checkAndCreatePopupPeer();
  71         updateImage();
  72     }
  73 
  74     private CPopupMenu checkAndCreatePopupPeer() {
  75         CPopupMenu menuPeer = null;
  76         if (popup != null) {
  77             try {
  78                 final MenuComponentAccessor acc = getMenuComponentAccessor();
  79                 menuPeer = acc.getPeer(popup);
  80                 if (menuPeer == null) {
  81                     popup.addNotify();
  82                     menuPeer = acc.getPeer(popup);
  83                 }


 112                 }
 113             } else {
 114                 return 0L;
 115             }
 116         }
 117 
 118         // This method is executed on Appkit, so if ptr is not zero means that,
 119         // it is still not deallocated(even if we call NSApp postRunnableEvent)
 120         // and sent CFRelease to the native queue
 121         return checkAndCreatePopupPeer().ptr;
 122     }
 123 
 124     /**
 125      * We display tray icon message as a small dialog with OK button.
 126      * This is lame, but JDK 1.6 does basically the same. There is a new
 127      * kind of window in Lion, NSPopover, so perhaps it could be used it
 128      * to implement better looking notifications.
 129      */
 130     public void displayMessage(final String caption, final String text,
 131                                final String messageType) {
 132 
 133         if (SwingUtilities.isEventDispatchThread()) {
 134             displayMessageOnEDT(caption, text, messageType);
 135         } else {
 136             try {
 137                 SwingUtilities.invokeAndWait(new Runnable() {
 138                     public void run() {
 139                         displayMessageOnEDT(caption, text, messageType);
 140                     }




 141                 });
 142             } catch (Exception e) {
 143                 throw new AssertionError(e);
 144             }
 145         }
 146     }
 147 
 148     @Override
 149     public void dispose() {
 150         if (messageDialog != null) {
 151             disposeMessageDialog();
 152         }
 153 
 154         dummyFrame.dispose();
 155 
 156         if (popup != null) {
 157             popup.removeNotify();
 158         }
 159 
 160         LWCToolkit.targetDisposedPeer(target, this);
 161         target = null;
 162 
 163         super.dispose();
 164     }
 165 
 166     @Override
 167     public void setToolTip(String tooltip) {
 168         execute(ptr -> nativeSetToolTip(ptr, tooltip));
 169     }
 170 
 171     //adds tooltip to the NSStatusBar's NSButton.
 172     private native void nativeSetToolTip(long trayIconModel, String tooltip);
 173 


 259             final String cmd = target.getActionCommand();
 260             final ActionEvent event = new ActionEvent(target,
 261                     ActionEvent.ACTION_PERFORMED, cmd);
 262             postEvent(event);
 263         }
 264 
 265         // synthesize CLICKED event
 266         if (jeventType == MouseEvent.MOUSE_RELEASED) {
 267             if ((mouseClickButtons & eventButtonMask) != 0) {
 268                 MouseEvent clickEvent = new MouseEvent(dummyFrame,
 269                         MouseEvent.MOUSE_CLICKED, when, jmodifiers, absX, absY,
 270                         absX, absY, jclickCount, isPopupTrigger, jbuttonNumber);
 271                 clickEvent.setSource(target);
 272                 postEvent(clickEvent);
 273             }
 274 
 275             mouseClickButtons &= ~eventButtonMask;
 276         }
 277     }
 278 
 279     private native Point2D nativeGetIconLocation(long trayIconModel);
 280 
 281     public void displayMessageOnEDT(String caption, String text,
 282                                     String messageType) {
 283         if (messageDialog != null) {
 284             disposeMessageDialog();
 285         }
 286 
 287         // obtain icon to show along the message
 288         Icon icon = getIconForMessageType(messageType);
 289         if (icon != null) {
 290             icon = new ImageIcon(scaleIcon(icon, 0.75));
 291         }
 292 
 293         // We want the message dialog text area to be about 1/8 of the screen
 294         // size. There is nothing special about this value, it's just makes the
 295         // message dialog to look nice
 296         Dimension screenSize = java.awt.Toolkit.getDefaultToolkit().getScreenSize();
 297         int textWidth = screenSize.width / 8;
 298 
 299         // create dialog to show
 300         messageDialog = createMessageDialog(caption, text, textWidth, icon);
 301 
 302         // finally, show the dialog to user
 303         showMessageDialog();
 304     }
 305 
 306     /**
 307      * Creates dialog window used to display the message
 308      */
 309     private JDialog createMessageDialog(String caption, String text,
 310                                      int textWidth, Icon icon) {
 311         JDialog dialog;
 312         handler = new DialogEventHandler();
 313 
 314         JTextArea captionArea = null;
 315         if (caption != null) {
 316             captionArea = createTextArea(caption, textWidth, false, true);
 317         }
 318 
 319         JTextArea textArea = null;
 320         if (text != null){
 321             textArea = createTextArea(text, textWidth, true, false);
 322         }
 323 
 324         Object[] panels = null;
 325         if (captionArea != null) {
 326             if (textArea != null) {
 327                 panels = new Object[] {captionArea, new JLabel(), textArea};
 328             } else {
 329                 panels = new Object[] {captionArea};
 330             }
 331         } else {
 332            if (textArea != null) {
 333                 panels = new Object[] {textArea};
 334             }
 335         }
 336 
 337         // We want message dialog with small title bar. There is a client
 338         // property property that does it, however, it must be set before
 339         // dialog's native window is created. This is why we create option
 340         // pane and dialog separately
 341         final JOptionPane op = new JOptionPane(panels);
 342         op.setIcon(icon);
 343         op.addPropertyChangeListener(handler);
 344 
 345         // Make Ok button small. Most likely won't work for L&F other then Aqua
 346         try {
 347             JPanel buttonPanel = (JPanel)op.getComponent(1);
 348             JButton ok = (JButton)buttonPanel.getComponent(0);
 349             ok.putClientProperty("JComponent.sizeVariant", "small");
 350         } catch (Throwable t) {
 351             // do nothing, we tried and failed, no big deal
 352         }
 353 
 354         dialog = new JDialog((Dialog) null);
 355         JRootPane rp = dialog.getRootPane();
 356 
 357         // gives us dialog window with small title bar and not zoomable
 358         rp.putClientProperty(CPlatformWindow.WINDOW_STYLE, "small");
 359         rp.putClientProperty(CPlatformWindow.WINDOW_ZOOMABLE, "false");
 360 
 361         dialog.setDefaultCloseOperation(JDialog.DO_NOTHING_ON_CLOSE);
 362         dialog.setModal(false);
 363         dialog.setModalExclusionType(Dialog.ModalExclusionType.TOOLKIT_EXCLUDE);
 364         dialog.setAlwaysOnTop(true);
 365         dialog.setAutoRequestFocus(false);
 366         dialog.setResizable(false);
 367         dialog.setContentPane(op);
 368 
 369         dialog.addWindowListener(handler);
 370 
 371         // suppress security warning for untrusted windows
 372         AWTAccessor.getWindowAccessor().setTrayIconWindow(dialog, true);
 373 
 374         dialog.pack();
 375 
 376         return dialog;
 377     }
 378 
 379     private void showMessageDialog() {
 380 
 381         Dimension screenSize = java.awt.Toolkit.getDefaultToolkit().getScreenSize();
 382         AtomicReference<Point2D> ref = new AtomicReference<>();
 383         execute(ptr -> {
 384             ref.set(nativeGetIconLocation(ptr));
 385         });
 386         Point2D iconLoc = ref.get();
 387         if (iconLoc == null) {
 388             return;
 389         }
 390 
 391         int dialogY = (int)iconLoc.getY();
 392         int dialogX = (int)iconLoc.getX();
 393         if (dialogX + messageDialog.getWidth() > screenSize.width) {
 394             dialogX = screenSize.width - messageDialog.getWidth();
 395         }
 396 
 397         messageDialog.setLocation(dialogX, dialogY);
 398         messageDialog.setVisible(true);
 399     }
 400 
 401    private void disposeMessageDialog() {
 402         if (SwingUtilities.isEventDispatchThread()) {
 403             disposeMessageDialogOnEDT();
 404         } else {
 405             try {
 406                 SwingUtilities.invokeAndWait(new Runnable() {
 407                     public void run() {
 408                         disposeMessageDialogOnEDT();
 409                     }
 410                 });
 411             } catch (Exception e) {
 412                 throw new AssertionError(e);
 413             }
 414         }
 415    }
 416 
 417     private void disposeMessageDialogOnEDT() {
 418         if (messageDialog != null) {
 419             messageDialog.removeWindowListener(handler);
 420             messageDialog.removePropertyChangeListener(handler);
 421             messageDialog.dispose();
 422 
 423             messageDialog = null;
 424             handler = null;
 425         }
 426     }
 427 
 428     /**
 429      * Scales an icon using specified scale factor
 430      *
 431      * @param icon        icon to scale
 432      * @param scaleFactor scale factor to use
 433      * @return scaled icon as BuffedredImage
 434      */
 435     private static BufferedImage scaleIcon(Icon icon, double scaleFactor) {
 436         if (icon == null) {
 437             return null;
 438         }
 439 
 440         int w = icon.getIconWidth();
 441         int h = icon.getIconHeight();
 442 
 443         GraphicsEnvironment ge =
 444                 GraphicsEnvironment.getLocalGraphicsEnvironment();
 445         GraphicsDevice gd = ge.getDefaultScreenDevice();
 446         GraphicsConfiguration gc = gd.getDefaultConfiguration();


 463         g.drawImage(iconImage, 0, 0, scaledW, scaledH, null);
 464         g.dispose();
 465 
 466         return scaledImage;
 467     }
 468 
 469 
 470     /**
 471      * Gets Aqua icon used in message dialog.
 472      */
 473     private static Icon getIconForMessageType(String messageType) {
 474         if (messageType.equals("ERROR")) {
 475             return UIManager.getIcon("OptionPane.errorIcon");
 476         } else if (messageType.equals("WARNING")) {
 477             return UIManager.getIcon("OptionPane.warningIcon");
 478         } else {
 479             // this is just an application icon
 480             return UIManager.getIcon("OptionPane.informationIcon");
 481         }
 482     }
 483 
 484     private static JTextArea createTextArea(String text, int width,
 485                                             boolean isSmall, boolean isBold) {
 486         JTextArea textArea = new JTextArea(text);
 487 
 488         textArea.setLineWrap(true);
 489         textArea.setWrapStyleWord(true);
 490         textArea.setEditable(false);
 491         textArea.setFocusable(false);
 492         textArea.setBorder(null);
 493         textArea.setBackground(new JLabel().getBackground());
 494 
 495         if (isSmall) {
 496             textArea.putClientProperty("JComponent.sizeVariant", "small");
 497         }
 498 
 499         if (isBold) {
 500             Font font = textArea.getFont();
 501             Font boldFont = new Font(font.getName(), Font.BOLD, font.getSize());
 502             textArea.setFont(boldFont);
 503         }
 504 
 505         textArea.setSize(width, 1);
 506 
 507         return textArea;
 508     }
 509 
 510     /**
 511      * Implements all the Listeners needed by message dialog
 512      */
 513     private final class DialogEventHandler extends WindowAdapter
 514             implements PropertyChangeListener {
 515 
 516         public void windowClosing(WindowEvent we) {
 517                 disposeMessageDialog();
 518         }
 519 
 520         public void propertyChange(PropertyChangeEvent e) {
 521             if (messageDialog == null) {
 522                 return;
 523             }
 524 
 525             String prop = e.getPropertyName();
 526             Container cp = messageDialog.getContentPane();
 527 
 528             if (messageDialog.isVisible() && e.getSource() == cp &&
 529                     (prop.equals(JOptionPane.VALUE_PROPERTY))) {
 530                 disposeMessageDialog();
 531             }
 532         }
 533     }
 534 }
 535 
   1 /*
   2  * Copyright (c) 2011, 2017, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package sun.lwawt.macosx;
  27 
  28 import java.awt.AWTEvent;
  29 import java.awt.Button;
  30 import java.awt.Frame;
  31 import java.awt.Graphics2D;
  32 import java.awt.GraphicsConfiguration;
  33 import java.awt.GraphicsDevice;
  34 import java.awt.GraphicsEnvironment;
  35 import java.awt.Image;
  36 import java.awt.MediaTracker;
  37 import java.awt.PopupMenu;
  38 import java.awt.RenderingHints;
  39 import java.awt.Toolkit;
  40 import java.awt.Transparency;
  41 import java.awt.TrayIcon;
  42 import java.awt.event.ActionEvent;
  43 import java.awt.event.MouseEvent;
  44 import java.awt.geom.Point2D;
  45 import java.awt.image.BufferedImage;
  46 import java.awt.peer.TrayIconPeer;



  47 
  48 import javax.swing.Icon;
  49 import javax.swing.UIManager;
  50 
  51 import sun.awt.SunToolkit;
  52 
  53 import static sun.awt.AWTAccessor.MenuComponentAccessor;
  54 import static sun.awt.AWTAccessor.getMenuComponentAccessor;
  55 
  56 public class CTrayIcon extends CFRetainedResource implements TrayIconPeer {
  57     private TrayIcon target;
  58     private PopupMenu popup;


  59 
  60     // In order to construct MouseEvent object, we need to specify a
  61     // Component target. Because TrayIcon isn't Component's subclass,
  62     // we use this dummy frame instead
  63     private final Frame dummyFrame;
  64 
  65     // A bitmask that indicates what mouse buttons produce MOUSE_CLICKED events
  66     // on MOUSE_RELEASE. Click events are only generated if there were no drag
  67     // events between MOUSE_PRESSED and MOUSE_RELEASED for particular button
  68     private static int mouseClickButtons = 0;
  69 
  70     CTrayIcon(TrayIcon target) {
  71         super(0, true);
  72 


  73         this.target = target;
  74         this.popup = target.getPopupMenu();
  75         this.dummyFrame = new Frame();
  76         setPtr(createModel());
  77 
  78         //if no one else is creating the peer.
  79         checkAndCreatePopupPeer();
  80         updateImage();
  81     }
  82 
  83     private CPopupMenu checkAndCreatePopupPeer() {
  84         CPopupMenu menuPeer = null;
  85         if (popup != null) {
  86             try {
  87                 final MenuComponentAccessor acc = getMenuComponentAccessor();
  88                 menuPeer = acc.getPeer(popup);
  89                 if (menuPeer == null) {
  90                     popup.addNotify();
  91                     menuPeer = acc.getPeer(popup);
  92                 }


 121                 }
 122             } else {
 123                 return 0L;
 124             }
 125         }
 126 
 127         // This method is executed on Appkit, so if ptr is not zero means that,
 128         // it is still not deallocated(even if we call NSApp postRunnableEvent)
 129         // and sent CFRelease to the native queue
 130         return checkAndCreatePopupPeer().ptr;
 131     }
 132 
 133     /**
 134      * We display tray icon message as a small dialog with OK button.
 135      * This is lame, but JDK 1.6 does basically the same. There is a new
 136      * kind of window in Lion, NSPopover, so perhaps it could be used it
 137      * to implement better looking notifications.
 138      */
 139     public void displayMessage(final String caption, final String text,
 140                                final String messageType) {
 141         // obtain icon to show along the message
 142         Icon icon = getIconForMessageType(messageType);
 143         CImage cimage = null;
 144         if (icon != null) {
 145             BufferedImage image = scaleIcon(icon, 0.75);
 146             cimage = CImage.getCreator().createFromImage(image);


 147         }
 148         if (cimage != null) {
 149             cimage.execute(imagePtr -> {
 150                 execute(ptr -> nativeShowNotification(ptr, caption, text,
 151                                                       imagePtr));
 152             });
 153         } else {
 154             execute(ptr -> nativeShowNotification(ptr, caption, text, 0));

 155         }
 156     }
 157 
 158     @Override
 159     public void dispose() {




 160         dummyFrame.dispose();
 161 
 162         if (popup != null) {
 163             popup.removeNotify();
 164         }
 165 
 166         LWCToolkit.targetDisposedPeer(target, this);
 167         target = null;
 168 
 169         super.dispose();
 170     }
 171 
 172     @Override
 173     public void setToolTip(String tooltip) {
 174         execute(ptr -> nativeSetToolTip(ptr, tooltip));
 175     }
 176 
 177     //adds tooltip to the NSStatusBar's NSButton.
 178     private native void nativeSetToolTip(long trayIconModel, String tooltip);
 179 


 265             final String cmd = target.getActionCommand();
 266             final ActionEvent event = new ActionEvent(target,
 267                     ActionEvent.ACTION_PERFORMED, cmd);
 268             postEvent(event);
 269         }
 270 
 271         // synthesize CLICKED event
 272         if (jeventType == MouseEvent.MOUSE_RELEASED) {
 273             if ((mouseClickButtons & eventButtonMask) != 0) {
 274                 MouseEvent clickEvent = new MouseEvent(dummyFrame,
 275                         MouseEvent.MOUSE_CLICKED, when, jmodifiers, absX, absY,
 276                         absX, absY, jclickCount, isPopupTrigger, jbuttonNumber);
 277                 clickEvent.setSource(target);
 278                 postEvent(clickEvent);
 279             }
 280 
 281             mouseClickButtons &= ~eventButtonMask;
 282         }
 283     }
 284 
 285     private native void nativeShowNotification(long trayIconModel,
 286                                                String caption, String text,
 287                                                long nsimage);























 288 
 289     /**
 290      * Used by the automated tests.
 291      */
 292     private native Point2D nativeGetIconLocation(long trayIconModel);





















































































































 293 
 294     /**
 295      * Scales an icon using specified scale factor
 296      *
 297      * @param icon        icon to scale
 298      * @param scaleFactor scale factor to use
 299      * @return scaled icon as BuffedredImage
 300      */
 301     private static BufferedImage scaleIcon(Icon icon, double scaleFactor) {
 302         if (icon == null) {
 303             return null;
 304         }
 305 
 306         int w = icon.getIconWidth();
 307         int h = icon.getIconHeight();
 308 
 309         GraphicsEnvironment ge =
 310                 GraphicsEnvironment.getLocalGraphicsEnvironment();
 311         GraphicsDevice gd = ge.getDefaultScreenDevice();
 312         GraphicsConfiguration gc = gd.getDefaultConfiguration();


 329         g.drawImage(iconImage, 0, 0, scaledW, scaledH, null);
 330         g.dispose();
 331 
 332         return scaledImage;
 333     }
 334 
 335 
 336     /**
 337      * Gets Aqua icon used in message dialog.
 338      */
 339     private static Icon getIconForMessageType(String messageType) {
 340         if (messageType.equals("ERROR")) {
 341             return UIManager.getIcon("OptionPane.errorIcon");
 342         } else if (messageType.equals("WARNING")) {
 343             return UIManager.getIcon("OptionPane.warningIcon");
 344         } else {
 345             // this is just an application icon
 346             return UIManager.getIcon("OptionPane.informationIcon");
 347         }
 348     }



















































 349 }
 350 
< prev index next >