--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.desktop/unix/classes/sun/awt/X11/InfoWindow.java Sun Aug 17 15:54:13 2014 +0100
@@ -0,0 +1,499 @@
+/*
+ * Copyright (c) 2009, 2014, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package sun.awt.X11;
+
+import java.awt.*;
+import java.awt.event.*;
+import java.awt.peer.TrayIconPeer;
+import sun.awt.*;
+import java.awt.image.*;
+import java.text.BreakIterator;
+import java.util.concurrent.ArrayBlockingQueue;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.lang.reflect.InvocationTargetException;
+
+/**
+ * An utility window class. This is a base class for Tooltip and Balloon.
+ */
+@SuppressWarnings("serial") // JDK-implementation class
+public abstract class InfoWindow extends Window {
+ private Container container;
+ private Closer closer;
+
+ protected InfoWindow(Frame parent, Color borderColor) {
+ super(parent);
+ setType(Window.Type.POPUP);
+ container = new Container() {
+ @Override
+ public Insets getInsets() {
+ return new Insets(1, 1, 1, 1);
+ }
+ };
+ setLayout(new BorderLayout());
+ setBackground(borderColor);
+ add(container, BorderLayout.CENTER);
+ container.setLayout(new BorderLayout());
+
+ closer = new Closer();
+ }
+
+ public Component add(Component c) {
+ container.add(c, BorderLayout.CENTER);
+ return c;
+ }
+
+ protected void setCloser(Runnable action, int time) {
+ closer.set(action, time);
+ }
+
+ // Must be executed on EDT.
+ protected void show(Point corner, int indent) {
+ assert SunToolkit.isDispatchThreadForAppContext(this);
+
+ pack();
+
+ Dimension size = getSize();
+ // TODO: When 6356322 is fixed we should get screen bounds in
+ // this way: eframe.getGraphicsConfiguration().getBounds().
+ Dimension scrSize = Toolkit.getDefaultToolkit().getScreenSize();
+
+ if (corner.x < scrSize.width/2 && corner.y < scrSize.height/2) { // 1st square
+ setLocation(corner.x + indent, corner.y + indent);
+
+ } else if (corner.x >= scrSize.width/2 && corner.y < scrSize.height/2) { // 2nd square
+ setLocation(corner.x - indent - size.width, corner.y + indent);
+
+ } else if (corner.x < scrSize.width/2 && corner.y >= scrSize.height/2) { // 3rd square
+ setLocation(corner.x + indent, corner.y - indent - size.height);
+
+ } else if (corner.x >= scrSize.width/2 && corner.y >= scrSize.height/2) { // 4th square
+ setLocation(corner.x - indent - size.width, corner.y - indent - size.height);
+ }
+
+ super.show();
+ closer.schedule();
+ }
+
+ public void hide() {
+ closer.close();
+ }
+
+ private class Closer implements Runnable {
+ Runnable action;
+ int time;
+
+ public void run() {
+ doClose();
+ }
+
+ void set(Runnable action, int time) {
+ this.action = action;
+ this.time = time;
+ }
+
+ void schedule() {
+ XToolkit.schedule(this, time);
+ }
+
+ void close() {
+ XToolkit.remove(this);
+ doClose();
+ }
+
+ // WARNING: this method may be executed on Toolkit thread.
+ private void doClose() {
+ SunToolkit.executeOnEventHandlerThread(InfoWindow.this, new Runnable() {
+ public void run() {
+ InfoWindow.super.hide();
+ invalidate();
+ if (action != null) {
+ action.run();
+ }
+ }
+ });
+ }
+ }
+
+
+ private interface LiveArguments {
+ /** Whether the target of the InfoWindow is disposed. */
+ boolean isDisposed();
+
+ /** The bounds of the target of the InfoWindow. */
+ Rectangle getBounds();
+ }
+
+ @SuppressWarnings("serial") // JDK-implementation class
+ public static class Tooltip extends InfoWindow {
+
+ public interface LiveArguments extends InfoWindow.LiveArguments {
+ /** The tooltip to be displayed. */
+ String getTooltipString();
+ }
+
+ private final Object target;
+ private final LiveArguments liveArguments;
+
+ private final Label textLabel = new Label("");
+ private final Runnable starter = new Runnable() {
+ public void run() {
+ display();
+ }};
+
+ private final static int TOOLTIP_SHOW_TIME = 10000;
+ private final static int TOOLTIP_START_DELAY_TIME = 1000;
+ private final static int TOOLTIP_MAX_LENGTH = 64;
+ private final static int TOOLTIP_MOUSE_CURSOR_INDENT = 5;
+ private final static Color TOOLTIP_BACKGROUND_COLOR = new Color(255, 255, 220);
+ private final static Font TOOLTIP_TEXT_FONT = XWindow.getDefaultFont();
+
+ public Tooltip(Frame parent, Object target,
+ LiveArguments liveArguments)
+ {
+ super(parent, Color.black);
+
+ this.target = target;
+ this.liveArguments = liveArguments;
+
+ XTrayIconPeer.suppressWarningString(this);
+
+ setCloser(null, TOOLTIP_SHOW_TIME);
+ textLabel.setBackground(TOOLTIP_BACKGROUND_COLOR);
+ textLabel.setFont(TOOLTIP_TEXT_FONT);
+ add(textLabel);
+ }
+
+ /*
+ * WARNING: this method is executed on Toolkit thread!
+ */
+ private void display() {
+ // Execute on EDT to avoid deadlock (see 6280857).
+ SunToolkit.executeOnEventHandlerThread(target, new Runnable() {
+ public void run() {
+ if (liveArguments.isDisposed()) {
+ return;
+ }
+
+ String tooltipString = liveArguments.getTooltipString();
+ if (tooltipString == null) {
+ return;
+ } else if (tooltipString.length() > TOOLTIP_MAX_LENGTH) {
+ textLabel.setText(tooltipString.substring(0, TOOLTIP_MAX_LENGTH));
+ } else {
+ textLabel.setText(tooltipString);
+ }
+
+ Point pointer = AccessController.doPrivileged(
+ new PrivilegedAction<Point>() {
+ public Point run() {
+ if (!isPointerOverTrayIcon(liveArguments.getBounds())) {
+ return null;
+ }
+ return MouseInfo.getPointerInfo().getLocation();
+ }
+ });
+ if (pointer == null) {
+ return;
+ }
+ show(new Point(pointer.x, pointer.y), TOOLTIP_MOUSE_CURSOR_INDENT);
+ }
+ });
+ }
+
+ public void enter() {
+ XToolkit.schedule(starter, TOOLTIP_START_DELAY_TIME);
+ }
+
+ public void exit() {
+ XToolkit.remove(starter);
+ if (isVisible()) {
+ hide();
+ }
+ }
+
+ private boolean isPointerOverTrayIcon(Rectangle trayRect) {
+ Point p = MouseInfo.getPointerInfo().getLocation();
+ return !(p.x < trayRect.x || p.x > (trayRect.x + trayRect.width) ||
+ p.y < trayRect.y || p.y > (trayRect.y + trayRect.height));
+ }
+ }
+
+ @SuppressWarnings("serial") // JDK-implementation class
+ public static class Balloon extends InfoWindow {
+
+ public interface LiveArguments extends InfoWindow.LiveArguments {
+ /** The action to be performed upon clicking the baloon. */
+ String getActionCommand();
+ }
+
+ private final LiveArguments liveArguments;
+ private final Object target;
+
+ private final static int BALLOON_SHOW_TIME = 10000;
+ private final static int BALLOON_TEXT_MAX_LENGTH = 256;
+ private final static int BALLOON_WORD_LINE_MAX_LENGTH = 16;
+ private final static int BALLOON_WORD_LINE_MAX_COUNT = 4;
+ private final static int BALLOON_ICON_WIDTH = 32;
+ private final static int BALLOON_ICON_HEIGHT = 32;
+ private final static int BALLOON_TRAY_ICON_INDENT = 0;
+ private final static Color BALLOON_CAPTION_BACKGROUND_COLOR = new Color(200, 200 ,255);
+ private final static Font BALLOON_CAPTION_FONT = new Font(Font.DIALOG, Font.BOLD, 12);
+
+ private Panel mainPanel = new Panel();
+ private Panel captionPanel = new Panel();
+ private Label captionLabel = new Label("");
+ private Button closeButton = new Button("X");
+ private Panel textPanel = new Panel();
+ private XTrayIconPeer.IconCanvas iconCanvas = new XTrayIconPeer.IconCanvas(BALLOON_ICON_WIDTH, BALLOON_ICON_HEIGHT);
+ private Label[] lineLabels = new Label[BALLOON_WORD_LINE_MAX_COUNT];
+ private ActionPerformer ap = new ActionPerformer();
+
+ private Image iconImage;
+ private Image errorImage;
+ private Image warnImage;
+ private Image infoImage;
+ private boolean gtkImagesLoaded;
+
+ private Displayer displayer = new Displayer();
+
+ public Balloon(Frame parent, Object target, LiveArguments liveArguments) {
+ super(parent, new Color(90, 80 ,190));
+ this.liveArguments = liveArguments;
+ this.target = target;
+
+ XTrayIconPeer.suppressWarningString(this);
+
+ setCloser(new Runnable() {
+ public void run() {
+ if (textPanel != null) {
+ textPanel.removeAll();
+ textPanel.setSize(0, 0);
+ iconCanvas.setSize(0, 0);
+ XToolkit.awtLock();
+ try {
+ displayer.isDisplayed = false;
+ XToolkit.awtLockNotifyAll();
+ } finally {
+ XToolkit.awtUnlock();
+ }
+ }
+ }
+ }, BALLOON_SHOW_TIME);
+
+ add(mainPanel);
+
+ captionLabel.setFont(BALLOON_CAPTION_FONT);
+ captionLabel.addMouseListener(ap);
+
+ captionPanel.setLayout(new BorderLayout());
+ captionPanel.add(captionLabel, BorderLayout.WEST);
+ captionPanel.add(closeButton, BorderLayout.EAST);
+ captionPanel.setBackground(BALLOON_CAPTION_BACKGROUND_COLOR);
+ captionPanel.addMouseListener(ap);
+
+ closeButton.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent e) {
+ hide();
+ }
+ });
+
+ mainPanel.setLayout(new BorderLayout());
+ mainPanel.setBackground(Color.white);
+ mainPanel.add(captionPanel, BorderLayout.NORTH);
+ mainPanel.add(iconCanvas, BorderLayout.WEST);
+ mainPanel.add(textPanel, BorderLayout.CENTER);
+
+ iconCanvas.addMouseListener(ap);
+
+ for (int i = 0; i < BALLOON_WORD_LINE_MAX_COUNT; i++) {
+ lineLabels[i] = new Label();
+ lineLabels[i].addMouseListener(ap);
+ lineLabels[i].setBackground(Color.white);
+ }
+
+ displayer.start();
+ }
+
+ public void display(String caption, String text, String messageType) {
+ if (!gtkImagesLoaded) {
+ loadGtkImages();
+ }
+ displayer.display(caption, text, messageType);
+ }
+
+ private void _display(String caption, String text, String messageType) {
+ captionLabel.setText(caption);
+
+ BreakIterator iter = BreakIterator.getWordInstance();
+ if (text != null) {
+ iter.setText(text);
+ int start = iter.first(), end;
+ int nLines = 0;
+
+ do {
+ end = iter.next();
+
+ if (end == BreakIterator.DONE ||
+ text.substring(start, end).length() >= 50)
+ {
+ lineLabels[nLines].setText(text.substring(start, end == BreakIterator.DONE ?
+ iter.last() : end));
+ textPanel.add(lineLabels[nLines++]);
+ start = end;
+ }
+ if (nLines == BALLOON_WORD_LINE_MAX_COUNT) {
+ if (end != BreakIterator.DONE) {
+ lineLabels[nLines - 1].setText(
+ new String(lineLabels[nLines - 1].getText() + " ..."));
+ }
+ break;
+ }
+ } while (end != BreakIterator.DONE);
+
+
+ textPanel.setLayout(new GridLayout(nLines, 1));
+ }
+
+ if ("ERROR".equals(messageType)) {
+ iconImage = errorImage;
+ } else if ("WARNING".equals(messageType)) {
+ iconImage = warnImage;
+ } else if ("INFO".equals(messageType)) {
+ iconImage = infoImage;
+ } else {
+ iconImage = null;
+ }
+
+ if (iconImage != null) {
+ Dimension tpSize = textPanel.getSize();
+ iconCanvas.setSize(BALLOON_ICON_WIDTH, (BALLOON_ICON_HEIGHT > tpSize.height ?
+ BALLOON_ICON_HEIGHT : tpSize.height));
+ iconCanvas.validate();
+ }
+
+ SunToolkit.executeOnEventHandlerThread(target, new Runnable() {
+ public void run() {
+ if (liveArguments.isDisposed()) {
+ return;
+ }
+ Point parLoc = getParent().getLocationOnScreen();
+ Dimension parSize = getParent().getSize();
+ show(new Point(parLoc.x + parSize.width/2, parLoc.y + parSize.height/2),
+ BALLOON_TRAY_ICON_INDENT);
+ if (iconImage != null) {
+ iconCanvas.updateImage(iconImage); // call it after the show(..) above
+ }
+ }
+ });
+ }
+
+ public void dispose() {
+ displayer.interrupt();
+ super.dispose();
+ }
+
+ private void loadGtkImages() {
+ if (!gtkImagesLoaded) {
+ errorImage = (Image)Toolkit.getDefaultToolkit().getDesktopProperty(
+ "gtk.icon.gtk-dialog-error.6.rtl");
+ warnImage = (Image)Toolkit.getDefaultToolkit().getDesktopProperty(
+ "gtk.icon.gtk-dialog-warning.6.rtl");
+ infoImage = (Image)Toolkit.getDefaultToolkit().getDesktopProperty(
+ "gtk.icon.gtk-dialog-info.6.rtl");
+ gtkImagesLoaded = true;
+ }
+ }
+
+ private class ActionPerformer extends MouseAdapter {
+ public void mouseClicked(MouseEvent e) {
+ // hide the balloon by any click
+ hide();
+ if (e.getButton() == MouseEvent.BUTTON1) {
+ ActionEvent aev = new ActionEvent(target, ActionEvent.ACTION_PERFORMED,
+ liveArguments.getActionCommand(),
+ e.getWhen(), e.getModifiers());
+ XToolkit.postEvent(XToolkit.targetToAppContext(aev.getSource()), aev);
+ }
+ }
+ }
+
+ private class Displayer extends Thread {
+ final int MAX_CONCURRENT_MSGS = 10;
+
+ ArrayBlockingQueue<Message> messageQueue = new ArrayBlockingQueue<Message>(MAX_CONCURRENT_MSGS);
+ boolean isDisplayed;
+
+ Displayer() {
+ setDaemon(true);
+ }
+
+ public void run() {
+ while (true) {
+ Message msg = null;
+ try {
+ msg = messageQueue.take();
+ } catch (InterruptedException e) {
+ return;
+ }
+
+ /*
+ * Wait till the previous message is displayed if any
+ */
+ XToolkit.awtLock();
+ try {
+ while (isDisplayed) {
+ try {
+ XToolkit.awtLockWait();
+ } catch (InterruptedException e) {
+ return;
+ }
+ }
+ isDisplayed = true;
+ } finally {
+ XToolkit.awtUnlock();
+ }
+ _display(msg.caption, msg.text, msg.messageType);
+ }
+ }
+
+ void display(String caption, String text, String messageType) {
+ messageQueue.offer(new Message(caption, text, messageType));
+ }
+ }
+
+ private static class Message {
+ String caption, text, messageType;
+
+ Message(String caption, String text, String messageType) {
+ this.caption = caption;
+ this.text = text;
+ this.messageType = messageType;
+ }
+ }
+ }
+}
+