jdk/src/solaris/classes/sun/awt/X11/InfoWindow.java
changeset 2472 b7aba00cabb6
child 3938 ef327bd847c0
equal deleted inserted replaced
2471:71401ceec494 2472:b7aba00cabb6
       
     1 /*
       
     2  * Copyright 2009 Sun Microsystems, Inc.  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.  Sun designates this
       
     8  * particular file as subject to the "Classpath" exception as provided
       
     9  * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
       
    22  * CA 95054 USA or visit www.sun.com if you need additional information or
       
    23  * have any questions.
       
    24  */
       
    25 
       
    26 package sun.awt.X11;
       
    27 
       
    28 import java.awt.*;
       
    29 import java.awt.event.*;
       
    30 import java.awt.peer.TrayIconPeer;
       
    31 import sun.awt.*;
       
    32 import java.awt.image.*;
       
    33 import java.text.BreakIterator;
       
    34 import java.util.logging.Logger;
       
    35 import java.util.logging.Level;
       
    36 import java.util.concurrent.ArrayBlockingQueue;
       
    37 import java.security.AccessController;
       
    38 import java.security.PrivilegedAction;
       
    39 import java.lang.reflect.InvocationTargetException;
       
    40 
       
    41 /**
       
    42  * An utility window class. This is a base class for Tooltip and Balloon.
       
    43  */
       
    44 public abstract class InfoWindow extends Window {
       
    45     private Container container;
       
    46     private Closer closer;
       
    47 
       
    48     protected InfoWindow(Frame parent, Color borderColor) {
       
    49         super(parent);
       
    50         container = new Container() {
       
    51             @Override
       
    52             public Insets getInsets() {
       
    53                 return new Insets(1, 1, 1, 1);
       
    54             }
       
    55         };
       
    56         setLayout(new BorderLayout());
       
    57         setBackground(borderColor);
       
    58         add(container, BorderLayout.CENTER);
       
    59         container.setLayout(new BorderLayout());
       
    60 
       
    61         closer = new Closer();
       
    62     }
       
    63 
       
    64     public Component add(Component c) {
       
    65         container.add(c, BorderLayout.CENTER);
       
    66         return c;
       
    67     }
       
    68 
       
    69     protected void setCloser(Runnable action, int time) {
       
    70         closer.set(action, time);
       
    71     }
       
    72 
       
    73     // Must be executed on EDT.
       
    74     protected void show(Point corner, int indent) {
       
    75         assert SunToolkit.isDispatchThreadForAppContext(this);
       
    76 
       
    77         pack();
       
    78 
       
    79         Dimension size = getSize();
       
    80         // TODO: When 6356322 is fixed we should get screen bounds in
       
    81         // this way: eframe.getGraphicsConfiguration().getBounds().
       
    82         Dimension scrSize = Toolkit.getDefaultToolkit().getScreenSize();
       
    83 
       
    84         if (corner.x < scrSize.width/2 && corner.y < scrSize.height/2) { // 1st square
       
    85             setLocation(corner.x + indent, corner.y + indent);
       
    86 
       
    87         } else if (corner.x >= scrSize.width/2 && corner.y < scrSize.height/2) { // 2nd square
       
    88             setLocation(corner.x - indent - size.width, corner.y + indent);
       
    89 
       
    90         } else if (corner.x < scrSize.width/2 && corner.y >= scrSize.height/2) { // 3rd square
       
    91             setLocation(corner.x + indent, corner.y - indent - size.height);
       
    92 
       
    93         } else if (corner.x >= scrSize.width/2 && corner.y >= scrSize.height/2) { // 4th square
       
    94             setLocation(corner.x - indent - size.width, corner.y - indent - size.height);
       
    95         }
       
    96 
       
    97         super.show();
       
    98         closer.schedule();
       
    99     }
       
   100 
       
   101     public void hide() {
       
   102         closer.close();
       
   103     }
       
   104 
       
   105     private class Closer implements Runnable {
       
   106         Runnable action;
       
   107         int time;
       
   108 
       
   109         public void run() {
       
   110             doClose();
       
   111         }
       
   112 
       
   113         void set(Runnable action, int time) {
       
   114             this.action = action;
       
   115             this.time = time;
       
   116         }
       
   117 
       
   118         void schedule() {
       
   119             XToolkit.schedule(this, time);
       
   120         }
       
   121 
       
   122         void close() {
       
   123             XToolkit.remove(this);
       
   124             doClose();
       
   125         }
       
   126 
       
   127         // WARNING: this method may be executed on Toolkit thread.
       
   128         private void doClose() {
       
   129             SunToolkit.executeOnEventHandlerThread(InfoWindow.this, new Runnable() {
       
   130                 public void run() {
       
   131                     InfoWindow.super.hide();
       
   132                     invalidate();
       
   133                     if (action != null) {
       
   134                         action.run();
       
   135                     }
       
   136                 }
       
   137             });
       
   138         }
       
   139     }
       
   140 
       
   141 
       
   142     private interface LiveArguments {
       
   143         /** Whether the target of the InfoWindow is disposed. */
       
   144         boolean isDisposed();
       
   145 
       
   146         /** The bounds of the target of the InfoWindow. */
       
   147         Rectangle getBounds();
       
   148     }
       
   149 
       
   150     public static class Tooltip extends InfoWindow {
       
   151 
       
   152         public interface LiveArguments extends InfoWindow.LiveArguments {
       
   153             /** The tooltip to be displayed. */
       
   154             String getTooltipString();
       
   155         }
       
   156 
       
   157         private final Object target;
       
   158         private final LiveArguments liveArguments;
       
   159 
       
   160         private final Label textLabel = new Label("");
       
   161         private final Runnable starter = new Runnable() {
       
   162                 public void run() {
       
   163                     display();
       
   164                 }};
       
   165 
       
   166         private final static int TOOLTIP_SHOW_TIME = 10000;
       
   167         private final static int TOOLTIP_START_DELAY_TIME = 1000;
       
   168         private final static int TOOLTIP_MAX_LENGTH = 64;
       
   169         private final static int TOOLTIP_MOUSE_CURSOR_INDENT = 5;
       
   170         private final static Color TOOLTIP_BACKGROUND_COLOR = new Color(255, 255, 220);
       
   171         private final static Font TOOLTIP_TEXT_FONT = XWindow.getDefaultFont();
       
   172 
       
   173         public Tooltip(Frame parent, Object target,
       
   174                 LiveArguments liveArguments)
       
   175         {
       
   176             super(parent, Color.black);
       
   177 
       
   178             this.target = target;
       
   179             this.liveArguments = liveArguments;
       
   180 
       
   181             XTrayIconPeer.suppressWarningString(this);
       
   182 
       
   183             setCloser(null, TOOLTIP_SHOW_TIME);
       
   184             textLabel.setBackground(TOOLTIP_BACKGROUND_COLOR);
       
   185             textLabel.setFont(TOOLTIP_TEXT_FONT);
       
   186             add(textLabel);
       
   187         }
       
   188 
       
   189         /*
       
   190          * WARNING: this method is executed on Toolkit thread!
       
   191          */
       
   192         private void display() {
       
   193             String tooltipString = liveArguments.getTooltipString();
       
   194             if (tooltipString == null) {
       
   195                 return;
       
   196             } else if (tooltipString.length() >  TOOLTIP_MAX_LENGTH) {
       
   197                 textLabel.setText(tooltipString.substring(0, TOOLTIP_MAX_LENGTH));
       
   198             } else {
       
   199                 textLabel.setText(tooltipString);
       
   200             }
       
   201 
       
   202             // Execute on EDT to avoid deadlock (see 6280857).
       
   203             SunToolkit.executeOnEventHandlerThread(target, new Runnable() {
       
   204                     public void run() {
       
   205                         if (liveArguments.isDisposed()) {
       
   206                             return;
       
   207                         }
       
   208                         Point pointer = (Point)AccessController.doPrivileged(new PrivilegedAction() {
       
   209                                 public Object run() {
       
   210                                     if (!isPointerOverTrayIcon(liveArguments.getBounds())) {
       
   211                                         return null;
       
   212                                     }
       
   213                                     return MouseInfo.getPointerInfo().getLocation();
       
   214                                 }
       
   215                             });
       
   216                         if (pointer == null) {
       
   217                             return;
       
   218                         }
       
   219                         show(new Point(pointer.x, pointer.y), TOOLTIP_MOUSE_CURSOR_INDENT);
       
   220                     }
       
   221                 });
       
   222         }
       
   223 
       
   224         public void enter() {
       
   225             XToolkit.schedule(starter, TOOLTIP_START_DELAY_TIME);
       
   226         }
       
   227 
       
   228         public void exit() {
       
   229             XToolkit.remove(starter);
       
   230             if (isVisible()) {
       
   231                 hide();
       
   232             }
       
   233         }
       
   234 
       
   235         private boolean isPointerOverTrayIcon(Rectangle trayRect) {
       
   236             Point p = MouseInfo.getPointerInfo().getLocation();
       
   237             return !(p.x < trayRect.x || p.x > (trayRect.x + trayRect.width) ||
       
   238                      p.y < trayRect.y || p.y > (trayRect.y + trayRect.height));
       
   239         }
       
   240     }
       
   241 
       
   242     public static class Balloon extends InfoWindow {
       
   243 
       
   244         public interface LiveArguments extends InfoWindow.LiveArguments {
       
   245             /** The action to be performed upon clicking the baloon. */
       
   246             String getActionCommand();
       
   247         }
       
   248 
       
   249         private final LiveArguments liveArguments;
       
   250         private final Object target;
       
   251 
       
   252         private final static int BALLOON_SHOW_TIME = 10000;
       
   253         private final static int BALLOON_TEXT_MAX_LENGTH = 256;
       
   254         private final static int BALLOON_WORD_LINE_MAX_LENGTH = 16;
       
   255         private final static int BALLOON_WORD_LINE_MAX_COUNT = 4;
       
   256         private final static int BALLOON_ICON_WIDTH = 32;
       
   257         private final static int BALLOON_ICON_HEIGHT = 32;
       
   258         private final static int BALLOON_TRAY_ICON_INDENT = 0;
       
   259         private final static Color BALLOON_CAPTION_BACKGROUND_COLOR = new Color(200, 200 ,255);
       
   260         private final static Font BALLOON_CAPTION_FONT = new Font(Font.DIALOG, Font.BOLD, 12);
       
   261 
       
   262         private Panel mainPanel = new Panel();
       
   263         private Panel captionPanel = new Panel();
       
   264         private Label captionLabel = new Label("");
       
   265         private Button closeButton = new Button("X");
       
   266         private Panel textPanel = new Panel();
       
   267         private XTrayIconPeer.IconCanvas iconCanvas = new XTrayIconPeer.IconCanvas(BALLOON_ICON_WIDTH, BALLOON_ICON_HEIGHT);
       
   268         private Label[] lineLabels = new Label[BALLOON_WORD_LINE_MAX_COUNT];
       
   269         private ActionPerformer ap = new ActionPerformer();
       
   270 
       
   271         private Image iconImage;
       
   272         private Image errorImage;
       
   273         private Image warnImage;
       
   274         private Image infoImage;
       
   275         private boolean gtkImagesLoaded;
       
   276 
       
   277         private Displayer displayer = new Displayer();
       
   278 
       
   279         public Balloon(Frame parent, Object target, LiveArguments liveArguments) {
       
   280             super(parent, new Color(90, 80 ,190));
       
   281             this.liveArguments = liveArguments;
       
   282             this.target = target;
       
   283 
       
   284             XTrayIconPeer.suppressWarningString(this);
       
   285 
       
   286             setCloser(new Runnable() {
       
   287                     public void run() {
       
   288                         if (textPanel != null) {
       
   289                             textPanel.removeAll();
       
   290                             textPanel.setSize(0, 0);
       
   291                             iconCanvas.setSize(0, 0);
       
   292                             XToolkit.awtLock();
       
   293                             try {
       
   294                                 displayer.isDisplayed = false;
       
   295                                 XToolkit.awtLockNotifyAll();
       
   296                             } finally {
       
   297                                 XToolkit.awtUnlock();
       
   298                             }
       
   299                         }
       
   300                     }
       
   301                 }, BALLOON_SHOW_TIME);
       
   302 
       
   303             add(mainPanel);
       
   304 
       
   305             captionLabel.setFont(BALLOON_CAPTION_FONT);
       
   306             captionLabel.addMouseListener(ap);
       
   307 
       
   308             captionPanel.setLayout(new BorderLayout());
       
   309             captionPanel.add(captionLabel, BorderLayout.WEST);
       
   310             captionPanel.add(closeButton, BorderLayout.EAST);
       
   311             captionPanel.setBackground(BALLOON_CAPTION_BACKGROUND_COLOR);
       
   312             captionPanel.addMouseListener(ap);
       
   313 
       
   314             closeButton.addActionListener(new ActionListener() {
       
   315                     public void actionPerformed(ActionEvent e) {
       
   316                         hide();
       
   317                     }
       
   318                 });
       
   319 
       
   320             mainPanel.setLayout(new BorderLayout());
       
   321             mainPanel.setBackground(Color.white);
       
   322             mainPanel.add(captionPanel, BorderLayout.NORTH);
       
   323             mainPanel.add(iconCanvas, BorderLayout.WEST);
       
   324             mainPanel.add(textPanel, BorderLayout.CENTER);
       
   325 
       
   326             iconCanvas.addMouseListener(ap);
       
   327 
       
   328             for (int i = 0; i < BALLOON_WORD_LINE_MAX_COUNT; i++) {
       
   329                 lineLabels[i] = new Label();
       
   330                 lineLabels[i].addMouseListener(ap);
       
   331                 lineLabels[i].setBackground(Color.white);
       
   332             }
       
   333 
       
   334             displayer.start();
       
   335         }
       
   336 
       
   337         public void display(String caption, String text, String messageType) {
       
   338             if (!gtkImagesLoaded) {
       
   339                 loadGtkImages();
       
   340             }
       
   341             displayer.display(caption, text, messageType);
       
   342         }
       
   343 
       
   344         private void _display(String caption, String text, String messageType) {
       
   345             captionLabel.setText(caption);
       
   346 
       
   347             BreakIterator iter = BreakIterator.getWordInstance();
       
   348             if (text != null) {
       
   349                 iter.setText(text);
       
   350                 int start = iter.first(), end;
       
   351                 int nLines = 0;
       
   352 
       
   353                 do {
       
   354                     end = iter.next();
       
   355 
       
   356                     if (end == BreakIterator.DONE ||
       
   357                         text.substring(start, end).length() >= 50)
       
   358                     {
       
   359                         lineLabels[nLines].setText(text.substring(start, end == BreakIterator.DONE ?
       
   360                                                                   iter.last() : end));
       
   361                         textPanel.add(lineLabels[nLines++]);
       
   362                         start = end;
       
   363                     }
       
   364                     if (nLines == BALLOON_WORD_LINE_MAX_COUNT) {
       
   365                         if (end != BreakIterator.DONE) {
       
   366                             lineLabels[nLines - 1].setText(
       
   367                                 new String(lineLabels[nLines - 1].getText() + " ..."));
       
   368                         }
       
   369                         break;
       
   370                     }
       
   371                 } while (end != BreakIterator.DONE);
       
   372 
       
   373 
       
   374                 textPanel.setLayout(new GridLayout(nLines, 1));
       
   375             }
       
   376 
       
   377             if ("ERROR".equals(messageType)) {
       
   378                 iconImage = errorImage;
       
   379             } else if ("WARNING".equals(messageType)) {
       
   380                 iconImage = warnImage;
       
   381             } else if ("INFO".equals(messageType)) {
       
   382                 iconImage = infoImage;
       
   383             } else {
       
   384                 iconImage = null;
       
   385             }
       
   386 
       
   387             if (iconImage != null) {
       
   388                 Dimension tpSize = textPanel.getSize();
       
   389                 iconCanvas.setSize(BALLOON_ICON_WIDTH, (BALLOON_ICON_HEIGHT > tpSize.height ?
       
   390                                                         BALLOON_ICON_HEIGHT : tpSize.height));
       
   391                 iconCanvas.validate();
       
   392             }
       
   393 
       
   394             SunToolkit.executeOnEventHandlerThread(target, new Runnable() {
       
   395                     public void run() {
       
   396                         if (liveArguments.isDisposed()) {
       
   397                             return;
       
   398                         }
       
   399                         Point parLoc = getParent().getLocationOnScreen();
       
   400                         Dimension parSize = getParent().getSize();
       
   401                         show(new Point(parLoc.x + parSize.width/2, parLoc.y + parSize.height/2),
       
   402                              BALLOON_TRAY_ICON_INDENT);
       
   403                         if (iconImage != null) {
       
   404                             iconCanvas.updateImage(iconImage); // call it after the show(..) above
       
   405                         }
       
   406                     }
       
   407                 });
       
   408         }
       
   409 
       
   410         public void dispose() {
       
   411             displayer.interrupt();
       
   412             super.dispose();
       
   413         }
       
   414 
       
   415         private void loadGtkImages() {
       
   416             if (!gtkImagesLoaded) {
       
   417                 errorImage = (Image)Toolkit.getDefaultToolkit().getDesktopProperty(
       
   418                     "gtk.icon.gtk-dialog-error.6.rtl");
       
   419                 warnImage = (Image)Toolkit.getDefaultToolkit().getDesktopProperty(
       
   420                     "gtk.icon.gtk-dialog-warning.6.rtl");
       
   421                 infoImage = (Image)Toolkit.getDefaultToolkit().getDesktopProperty(
       
   422                     "gtk.icon.gtk-dialog-info.6.rtl");
       
   423                 gtkImagesLoaded = true;
       
   424             }
       
   425         }
       
   426 
       
   427         private class ActionPerformer extends MouseAdapter {
       
   428             public void mouseClicked(MouseEvent e) {
       
   429                 // hide the balloon by any click
       
   430                 hide();
       
   431                 if (e.getButton() == MouseEvent.BUTTON1) {
       
   432                     ActionEvent aev = new ActionEvent(target, ActionEvent.ACTION_PERFORMED,
       
   433                                                       liveArguments.getActionCommand(),
       
   434                                                       e.getWhen(), e.getModifiers());
       
   435                     Toolkit.getDefaultToolkit().getSystemEventQueue().postEvent(aev);
       
   436                 }
       
   437             }
       
   438         }
       
   439 
       
   440         private class Displayer extends Thread {
       
   441             final int MAX_CONCURRENT_MSGS = 10;
       
   442 
       
   443             ArrayBlockingQueue<Message> messageQueue = new ArrayBlockingQueue<Message>(MAX_CONCURRENT_MSGS);
       
   444             boolean isDisplayed;
       
   445 
       
   446             Displayer() {
       
   447                 setDaemon(true);
       
   448             }
       
   449 
       
   450             public void run() {
       
   451                 while (true) {
       
   452                     Message msg = null;
       
   453                     try {
       
   454                         msg = (Message)messageQueue.take();
       
   455                     } catch (InterruptedException e) {
       
   456                         return;
       
   457                     }
       
   458 
       
   459                     /*
       
   460                      * Wait till the previous message is displayed if any
       
   461                      */
       
   462                     XToolkit.awtLock();
       
   463                     try {
       
   464                         while (isDisplayed) {
       
   465                             try {
       
   466                                 XToolkit.awtLockWait();
       
   467                             } catch (InterruptedException e) {
       
   468                                 return;
       
   469                             }
       
   470                         }
       
   471                         isDisplayed = true;
       
   472                     } finally {
       
   473                         XToolkit.awtUnlock();
       
   474                     }
       
   475                     _display(msg.caption, msg.text, msg.messageType);
       
   476                 }
       
   477             }
       
   478 
       
   479             void display(String caption, String text, String messageType) {
       
   480                 messageQueue.offer(new Message(caption, text, messageType));
       
   481             }
       
   482         }
       
   483 
       
   484         private static class Message {
       
   485             String caption, text, messageType;
       
   486 
       
   487             Message(String caption, String text, String messageType) {
       
   488                 this.caption = caption;
       
   489                 this.text = text;
       
   490                 this.messageType = messageType;
       
   491             }
       
   492         }
       
   493     }
       
   494 }
       
   495