From 49aa62455c427c6cb27659e43e55bdd480aef202 Mon Sep 17 00:00:00 2001 From: Sean Chou Date: Thu, 20 Sep 2012 17:39:47 +0400 Subject: [PATCH 1/9] 7194184: JColorChooser swatch cannot accessed from keyboard Reviewed-by: rupashka, alexsch --- .../DefaultSwatchChooserPanel.java | 138 ++++++++++++++++-- .../swing/JColorChooser/Test7194184.java | 113 ++++++++++++++ 2 files changed, 242 insertions(+), 9 deletions(-) create mode 100644 jdk/test/javax/swing/JColorChooser/Test7194184.java diff --git a/jdk/src/share/classes/javax/swing/colorchooser/DefaultSwatchChooserPanel.java b/jdk/src/share/classes/javax/swing/colorchooser/DefaultSwatchChooserPanel.java index a0227dd716c..564a085ea98 100644 --- a/jdk/src/share/classes/javax/swing/colorchooser/DefaultSwatchChooserPanel.java +++ b/jdk/src/share/classes/javax/swing/colorchooser/DefaultSwatchChooserPanel.java @@ -57,6 +57,8 @@ class DefaultSwatchChooserPanel extends AbstractColorChooserPanel { RecentSwatchPanel recentSwatchPanel; MouseListener mainSwatchListener; MouseListener recentSwatchListener; + private KeyListener mainSwatchKeyListener; + private KeyListener recentSwatchKeyListener; public DefaultSwatchChooserPanel() { super(); @@ -151,10 +153,14 @@ class DefaultSwatchChooserPanel extends AbstractColorChooserPanel { recentSwatchPanel.putClientProperty(AccessibleContext.ACCESSIBLE_NAME_PROPERTY, recentStr); + mainSwatchKeyListener = new MainSwatchKeyListener(); mainSwatchListener = new MainSwatchListener(); swatchPanel.addMouseListener(mainSwatchListener); + swatchPanel.addKeyListener(mainSwatchKeyListener); recentSwatchListener = new RecentSwatchListener(); + recentSwatchKeyListener = new RecentSwatchKeyListener(); recentSwatchPanel.addMouseListener(recentSwatchListener); + recentSwatchPanel.addKeyListener(recentSwatchKeyListener); JPanel mainHolder = new JPanel(new BorderLayout()); Border border = new CompoundBorder( new LineBorder(Color.black), @@ -196,11 +202,17 @@ class DefaultSwatchChooserPanel extends AbstractColorChooserPanel { public void uninstallChooserPanel(JColorChooser enclosingChooser) { super.uninstallChooserPanel(enclosingChooser); swatchPanel.removeMouseListener(mainSwatchListener); + swatchPanel.removeKeyListener(mainSwatchKeyListener); recentSwatchPanel.removeMouseListener(recentSwatchListener); + recentSwatchPanel.removeKeyListener(recentSwatchKeyListener); + swatchPanel = null; recentSwatchPanel = null; mainSwatchListener = null; + mainSwatchKeyListener = null; recentSwatchListener = null; + recentSwatchKeyListener = null; + removeAll(); // strip out all the sub-components } @@ -209,11 +221,32 @@ class DefaultSwatchChooserPanel extends AbstractColorChooserPanel { } + private class RecentSwatchKeyListener extends KeyAdapter { + public void keyPressed(KeyEvent e) { + if (KeyEvent.VK_SPACE == e.getKeyCode()) { + Color color = recentSwatchPanel.getSelectedColor(); + setSelectedColor(color); + } + } + } + + private class MainSwatchKeyListener extends KeyAdapter { + public void keyPressed(KeyEvent e) { + if (KeyEvent.VK_SPACE == e.getKeyCode()) { + Color color = swatchPanel.getSelectedColor(); + setSelectedColor(color); + recentSwatchPanel.setMostRecentColor(color); + } + } + } + class RecentSwatchListener extends MouseAdapter implements Serializable { public void mousePressed(MouseEvent e) { if (isEnabled()) { Color color = recentSwatchPanel.getColorForLocation(e.getX(), e.getY()); + recentSwatchPanel.setSelectedColorFromLocation(e.getX(), e.getY()); setSelectedColor(color); + recentSwatchPanel.requestFocusInWindow(); } } } @@ -223,7 +256,9 @@ class DefaultSwatchChooserPanel extends AbstractColorChooserPanel { if (isEnabled()) { Color color = swatchPanel.getColorForLocation(e.getX(), e.getY()); setSelectedColor(color); + swatchPanel.setSelectedColorFromLocation(e.getX(), e.getY()); recentSwatchPanel.setMostRecentColor(color); + swatchPanel.requestFocusInWindow(); } } } @@ -239,18 +274,81 @@ class SwatchPanel extends JPanel { protected Dimension numSwatches; protected Dimension gap; + private int selRow; + private int selCol; + public SwatchPanel() { initValues(); initColors(); setToolTipText(""); // register for events setOpaque(true); setBackground(Color.white); - setRequestFocusEnabled(false); + setFocusable(true); setInheritsPopupMenu(true); + + addFocusListener(new FocusAdapter() { + public void focusGained(FocusEvent e) { + repaint(); + } + + public void focusLost(FocusEvent e) { + repaint(); + } + }); + + addKeyListener(new KeyAdapter() { + public void keyPressed(KeyEvent e) { + int typed = e.getKeyCode(); + switch (typed) { + case KeyEvent.VK_UP: + if (selRow > 0) { + selRow--; + repaint(); + } + break; + case KeyEvent.VK_DOWN: + if (selRow < numSwatches.height - 1) { + selRow++; + repaint(); + } + break; + case KeyEvent.VK_LEFT: + if (selCol > 0 && SwatchPanel.this.getComponentOrientation().isLeftToRight()) { + selCol--; + repaint(); + } else if (selCol < numSwatches.width - 1 + && !SwatchPanel.this.getComponentOrientation().isLeftToRight()) { + selCol++; + repaint(); + } + break; + case KeyEvent.VK_RIGHT: + if (selCol < numSwatches.width - 1 + && SwatchPanel.this.getComponentOrientation().isLeftToRight()) { + selCol++; + repaint(); + } else if (selCol > 0 && !SwatchPanel.this.getComponentOrientation().isLeftToRight()) { + selCol--; + repaint(); + } + break; + case KeyEvent.VK_HOME: + selCol = 0; + selRow = 0; + repaint(); + break; + case KeyEvent.VK_END: + selCol = numSwatches.width - 1; + selRow = numSwatches.height - 1; + repaint(); + break; + } + } + }); } - public boolean isFocusTraversable() { - return false; + public Color getSelectedColor() { + return getColorForCell(selCol, selRow); } protected void initValues() { @@ -263,11 +361,10 @@ class SwatchPanel extends JPanel { for (int row = 0; row < numSwatches.height; row++) { int y = row * (swatchSize.height + gap.height); for (int column = 0; column < numSwatches.width; column++) { - - g.setColor( getColorForCell(column, row) ); + Color c = getColorForCell(column, row); + g.setColor(c); int x; - if ((!this.getComponentOrientation().isLeftToRight()) && - (this instanceof RecentSwatchPanel)) { + if (!this.getComponentOrientation().isLeftToRight()) { x = (numSwatches.width - column - 1) * (swatchSize.width + gap.width); } else { x = column * (swatchSize.width + gap.width); @@ -276,6 +373,20 @@ class SwatchPanel extends JPanel { g.setColor(Color.black); g.drawLine( x+swatchSize.width-1, y, x+swatchSize.width-1, y+swatchSize.height-1); g.drawLine( x, y+swatchSize.height-1, x+swatchSize.width-1, y+swatchSize.height-1); + + if (selRow == row && selCol == column && this.isFocusOwner()) { + Color c2 = new Color(c.getRed() < 125 ? 255 : 0, + c.getGreen() < 125 ? 255 : 0, + c.getBlue() < 125 ? 255 : 0); + g.setColor(c2); + + g.drawLine(x, y, x + swatchSize.width - 1, y); + g.drawLine(x, y, x, y + swatchSize.height - 1); + g.drawLine(x + swatchSize.width - 1, y, x + swatchSize.width - 1, y + swatchSize.height - 1); + g.drawLine(x, y + swatchSize.height - 1, x + swatchSize.width - 1, y + swatchSize.height - 1); + g.drawLine(x, y, x + swatchSize.width - 1, y + swatchSize.height - 1); + g.drawLine(x, y + swatchSize.height - 1, x + swatchSize.width - 1, y); + } } } } @@ -296,10 +407,19 @@ class SwatchPanel extends JPanel { return color.getRed()+", "+ color.getGreen() + ", " + color.getBlue(); } + public void setSelectedColorFromLocation(int x, int y) { + if (!this.getComponentOrientation().isLeftToRight()) { + selCol = numSwatches.width - x / (swatchSize.width + gap.width) - 1; + } else { + selCol = x / (swatchSize.width + gap.width); + } + selRow = y / (swatchSize.height + gap.height); + repaint(); + } + public Color getColorForLocation( int x, int y ) { int column; - if ((!this.getComponentOrientation().isLeftToRight()) && - (this instanceof RecentSwatchPanel)) { + if (!this.getComponentOrientation().isLeftToRight()) { column = numSwatches.width - x / (swatchSize.width + gap.width) - 1; } else { column = x / (swatchSize.width + gap.width); diff --git a/jdk/test/javax/swing/JColorChooser/Test7194184.java b/jdk/test/javax/swing/JColorChooser/Test7194184.java new file mode 100644 index 00000000000..97599b12696 --- /dev/null +++ b/jdk/test/javax/swing/JColorChooser/Test7194184.java @@ -0,0 +1,113 @@ +/* + * Copyright (c) 2012, 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. + * + * 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. + */ + +/* + * Portions Copyright (c) 2012 IBM Corporation + */ + +/* + * @test + * @bug 7194184 + * @summary Tests JColorChooser Swatch keyboard accessibility. + * @author Sean Chou + * @library ../regtesthelpers + * @build Util + * @run main Test7194184 + */ + +import java.awt.Component; +import java.awt.AWTException; +import java.awt.Color; +import java.awt.Robot; +import java.awt.Toolkit; +import java.awt.event.KeyEvent; + +import javax.swing.JColorChooser; +import javax.swing.JFrame; +import javax.swing.SwingUtilities; + +import java.util.concurrent.Callable; +import sun.awt.SunToolkit; + +public class Test7194184 implements Runnable { + private static JFrame frame; + private static JColorChooser colorChooser; + private static Color selectedColor; + + public static void main(String[] args) throws Exception { + testKeyBoardAccess(); + } + + private static void testKeyBoardAccess() throws Exception { + Robot robot = new Robot(); + SunToolkit toolkit = (SunToolkit) Toolkit.getDefaultToolkit(); + + SwingUtilities.invokeLater(new Test7194184()); + toolkit.realSync(); + + SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + selectedColor = colorChooser.getColor(); + + Component recentSwatchPanel = Util.findSubComponent(colorChooser, "RecentSwatchPanel"); + if (recentSwatchPanel == null) { + throw new RuntimeException("RecentSwatchPanel not found"); + } + recentSwatchPanel.requestFocusInWindow(); + } + }); + + toolkit.realSync(); + + // Tab to move the focus to MainSwatch + Util.hitKeys(robot, KeyEvent.VK_SHIFT, KeyEvent.VK_TAB); + + // Select the color on right + Util.hitKeys(robot, KeyEvent.VK_RIGHT); + Util.hitKeys(robot, KeyEvent.VK_RIGHT); + Util.hitKeys(robot, KeyEvent.VK_SPACE); + toolkit.realSync(); + + SwingUtilities.invokeAndWait(new Runnable() { + @Override + public void run() { + frame.dispose(); + if (selectedColor == colorChooser.getColor()) { + throw new RuntimeException("JColorChooser misses keyboard accessibility"); + } + } + }); + } + + public void run() { + String title = getClass().getName(); + frame = new JFrame(title); + colorChooser = new JColorChooser(); + + frame.add(colorChooser); + frame.pack(); + frame.setVisible(true); + } + +} From 211c061e3e6a41c7f4700cb6bcdcd7c2128540c3 Mon Sep 17 00:00:00 2001 From: Vladislav Karnaukhov Date: Thu, 20 Sep 2012 17:55:40 +0400 Subject: [PATCH 2/9] 7123767: Wrong tooltip location in Multi-Monitor configurations Reviewed-by: rupashka --- .../classes/javax/swing/ToolTipManager.java | 50 +++- .../ToolTipManager/7123767/bug7123767.java | 220 ++++++++++++++++++ 2 files changed, 262 insertions(+), 8 deletions(-) create mode 100644 jdk/test/javax/swing/ToolTipManager/7123767/bug7123767.java diff --git a/jdk/src/share/classes/javax/swing/ToolTipManager.java b/jdk/src/share/classes/javax/swing/ToolTipManager.java index fd0953dd252..1bb26e29cce 100644 --- a/jdk/src/share/classes/javax/swing/ToolTipManager.java +++ b/jdk/src/share/classes/javax/swing/ToolTipManager.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2011, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2012, 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 @@ -217,6 +217,25 @@ public class ToolTipManager extends MouseAdapter implements MouseMotionListener return exitTimer.getInitialDelay(); } + // Returns GraphicsConfiguration instance that toFind belongs to or null + // if drawing point is set to a point beyond visible screen area (e.g. + // Point(20000, 20000)) + private GraphicsConfiguration getDrawingGC(Point toFind) { + GraphicsEnvironment env = GraphicsEnvironment.getLocalGraphicsEnvironment(); + GraphicsDevice devices[] = env.getScreenDevices(); + for (GraphicsDevice device : devices) { + GraphicsConfiguration configs[] = device.getConfigurations(); + for (GraphicsConfiguration config : configs) { + Rectangle rect = config.getBounds(); + if (rect.contains(toFind)) { + return config; + } + } + } + + return null; + } + void showTipWindow() { if(insideComponent == null || !insideComponent.isShowing()) return; @@ -231,9 +250,25 @@ public class ToolTipManager extends MouseAdapter implements MouseMotionListener if (enabled) { Dimension size; Point screenLocation = insideComponent.getLocationOnScreen(); - Point location = new Point(); - GraphicsConfiguration gc; - gc = insideComponent.getGraphicsConfiguration(); + Point location; + + Point toFind; + if (preferredLocation != null) { + toFind = new Point(screenLocation.x + preferredLocation.x, + screenLocation.y + preferredLocation.y); + } else { + toFind = mouseEvent.getLocationOnScreen(); + } + + GraphicsConfiguration gc = getDrawingGC(toFind); + if (gc == null) { + toFind = mouseEvent.getLocationOnScreen(); + gc = getDrawingGC(toFind); + if (gc == null) { + gc = insideComponent.getGraphicsConfiguration(); + } + } + Rectangle sBounds = gc.getBounds(); Insets screenInsets = Toolkit.getDefaultToolkit() .getScreenInsets(gc); @@ -253,14 +288,13 @@ public class ToolTipManager extends MouseAdapter implements MouseMotionListener size = tip.getPreferredSize(); if(preferredLocation != null) { - location.x = screenLocation.x + preferredLocation.x; - location.y = screenLocation.y + preferredLocation.y; + location = toFind; if (!leftToRight) { location.x -= size.width; } } else { - location.x = screenLocation.x + mouseEvent.getX(); - location.y = screenLocation.y + mouseEvent.getY() + 20; + location = new Point(screenLocation.x + mouseEvent.getX(), + screenLocation.y + mouseEvent.getY() + 20); if (!leftToRight) { if(location.x - size.width>=0) { location.x -= size.width; diff --git a/jdk/test/javax/swing/ToolTipManager/7123767/bug7123767.java b/jdk/test/javax/swing/ToolTipManager/7123767/bug7123767.java new file mode 100644 index 00000000000..4c7402d3900 --- /dev/null +++ b/jdk/test/javax/swing/ToolTipManager/7123767/bug7123767.java @@ -0,0 +1,220 @@ +/* + * Copyright (c) 2012, 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. + * + * 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. + */ + +/* @test + @bug 7123767 + @summary Wrong tooltip location in Multi-Monitor configurations + @author Vladislav Karnaukhov + @run main bug7123767 +*/ + +import sun.awt.SunToolkit; + +import javax.swing.*; +import javax.swing.plaf.metal.MetalLookAndFeel; +import java.awt.*; +import java.awt.event.MouseEvent; +import java.lang.reflect.InvocationTargetException; + +public class bug7123767 extends JFrame { + + private static class TestFactory extends PopupFactory { + + private static TestFactory newFactory = new TestFactory(); + private static PopupFactory oldFactory; + + private TestFactory() { + super(); + } + + public static void install() { + if (oldFactory == null) { + oldFactory = getSharedInstance(); + setSharedInstance(newFactory); + } + } + + public static void uninstall() { + if (oldFactory != null) { + setSharedInstance(oldFactory); + } + } + + // Actual test happens here + public Popup getPopup(Component owner, Component contents, int x, int y) { + GraphicsConfiguration mouseGC = testGC(MouseInfo.getPointerInfo().getLocation()); + if (mouseGC == null) { + throw new RuntimeException("Can't find GraphicsConfiguration that mouse pointer belongs to"); + } + + GraphicsConfiguration tipGC = testGC(new Point(x, y)); + if (tipGC == null) { + throw new RuntimeException("Can't find GraphicsConfiguration that tip belongs to"); + } + + if (!mouseGC.equals(tipGC)) { + throw new RuntimeException("Mouse and tip GCs are not equal"); + } + + return super.getPopup(owner, contents, x, y); + } + + private static GraphicsConfiguration testGC(Point pt) { + GraphicsEnvironment environment = GraphicsEnvironment.getLocalGraphicsEnvironment(); + GraphicsDevice[] devices = environment.getScreenDevices(); + for (GraphicsDevice device : devices) { + GraphicsConfiguration[] configs = device.getConfigurations(); + for (GraphicsConfiguration config : configs) { + Rectangle rect = config.getBounds(); + Insets insets = Toolkit.getDefaultToolkit().getScreenInsets(config); + adjustInsets(rect, insets); + if (rect.contains(pt)) + return config; + } + } + + return null; + } + } + + private static final int MARGIN = 10; + private static bug7123767 frame; + private static Robot robot; + + public static void main(String[] args) throws Exception { + UIManager.setLookAndFeel(new MetalLookAndFeel()); + setUp(); + testToolTip(); + TestFactory.uninstall(); + } + + // Creates a window that is stretched across all available monitors + // and adds itself as ContainerListener to track tooltips drawing + private bug7123767() { + super(); + + ToolTipManager.sharedInstance().setInitialDelay(0); + setDefaultCloseOperation(DISPOSE_ON_CLOSE); + TestFactory.install(); + + JLabel label1 = new JLabel("no preferred location"); + label1.setToolTipText("tip"); + add(label1, BorderLayout.WEST); + + JLabel label2 = new JLabel("preferred location (20000, 20000)") { + public Point getToolTipLocation(MouseEvent event) { + return new Point(20000, 20000); + } + }; + + label2.setToolTipText("tip"); + add(label2, BorderLayout.EAST); + + setUndecorated(true); + pack(); + + Rectangle rect = new Rectangle(); + GraphicsEnvironment environment = GraphicsEnvironment.getLocalGraphicsEnvironment(); + GraphicsDevice[] devices = environment.getScreenDevices(); + for (GraphicsDevice device : devices) { + GraphicsConfiguration[] configs = device.getConfigurations(); + for (GraphicsConfiguration config : configs) { + Insets localInsets = Toolkit.getDefaultToolkit().getScreenInsets(config); + Rectangle localRect = config.getBounds(); + adjustInsets(localRect, localInsets); + rect.add(localRect); + } + } + setBounds(rect); + } + + private static void setUp() throws InterruptedException, InvocationTargetException { + SwingUtilities.invokeAndWait(new Runnable() { + @Override + public void run() { + frame = new bug7123767(); + frame.setVisible(true); + } + }); + } + + // Moves mouse pointer to the corners of every GraphicsConfiguration + private static void testToolTip() throws AWTException { + SunToolkit toolkit = (SunToolkit) Toolkit.getDefaultToolkit(); + toolkit.realSync(); + + GraphicsEnvironment environment = GraphicsEnvironment.getLocalGraphicsEnvironment(); + GraphicsDevice[] devices = environment.getScreenDevices(); + for (GraphicsDevice device : devices) { + GraphicsConfiguration[] configs = device.getConfigurations(); + for (GraphicsConfiguration config : configs) { + Rectangle rect = config.getBounds(); + Insets insets = toolkit.getScreenInsets(config); + adjustInsets(rect, insets); + + // Upper left + glide(rect.x + rect.width / 2, rect.y + rect.height / 2, + rect.x + MARGIN, rect.y + MARGIN); + toolkit.realSync(); + + // Lower left + glide(rect.x + rect.width / 2, rect.y + rect.height / 2, + rect.x + MARGIN, rect.y + rect.height - MARGIN); + toolkit.realSync(); + + // Upper right + glide(rect.x + rect.width / 2, rect.y + rect.height / 2, + rect.x + rect.width - MARGIN, rect.y + MARGIN); + toolkit.realSync(); + + // Lower right + glide(rect.x + rect.width / 2, rect.y + rect.height / 2, + rect.x + rect.width - MARGIN, rect.y + rect.height - MARGIN); + toolkit.realSync(); + } + } + } + + private static void glide(int x0, int y0, int x1, int y1) throws AWTException { + if (robot == null) { + robot = new Robot(); + robot.setAutoDelay(20); + } + + float dmax = (float) Math.max(Math.abs(x1 - x0), Math.abs(y1 - y0)); + float dx = (x1 - x0) / dmax; + float dy = (y1 - y0) / dmax; + + robot.mouseMove(x0, y0); + for (int i = 1; i <= dmax; i += 10) { + robot.mouseMove((int) (x0 + dx * i), (int) (y0 + dy * i)); + } + } + + private static void adjustInsets(Rectangle rect, final Insets insets) { + rect.x += insets.left; + rect.y += insets.top; + rect.width -= (insets.left + insets.right); + rect.height -= (insets.top + insets.bottom); + } +} From aca74a151c93bdde2e60cfe720926818daba2ad1 Mon Sep 17 00:00:00 2001 From: Alexander Scherbatiy Date: Fri, 21 Sep 2012 13:48:06 +0400 Subject: [PATCH 3/9] 7199180: [macosx] Dead keys handling for input methods Reviewed-by: kizune, anthony --- .../sun/lwawt/macosx/CPlatformResponder.java | 3 + jdk/src/macosx/native/sun/awt/AWTEvent.m | 7 +- jdk/src/macosx/native/sun/awt/AWTView.m | 5 +- .../DeadKey/DeadKeyMacOSXInputText.java | 131 ++++++++++++++++++ 4 files changed, 142 insertions(+), 4 deletions(-) create mode 100644 jdk/test/java/awt/event/KeyEvent/DeadKey/DeadKeyMacOSXInputText.java diff --git a/jdk/src/macosx/classes/sun/lwawt/macosx/CPlatformResponder.java b/jdk/src/macosx/classes/sun/lwawt/macosx/CPlatformResponder.java index d874b26fb70..a7580a6f0cd 100644 --- a/jdk/src/macosx/classes/sun/lwawt/macosx/CPlatformResponder.java +++ b/jdk/src/macosx/classes/sun/lwawt/macosx/CPlatformResponder.java @@ -160,6 +160,9 @@ final class CPlatformResponder { if(isDeadChar){ testChar = (char) out[2]; + if(testChar == 0){ + return; + } } jkeyCode = out[0]; diff --git a/jdk/src/macosx/native/sun/awt/AWTEvent.m b/jdk/src/macosx/native/sun/awt/AWTEvent.m index 85c9fa9718e..7bde46f1709 100644 --- a/jdk/src/macosx/native/sun/awt/AWTEvent.m +++ b/jdk/src/macosx/native/sun/awt/AWTEvent.m @@ -383,6 +383,7 @@ static unichar NsGetDeadKeyChar(unsigned short keyCode) { TISInputSourceRef currentKeyboard = TISCopyCurrentKeyboardInputSource(); CFDataRef uchr = (CFDataRef)TISGetInputSourceProperty(currentKeyboard, kTISPropertyUnicodeKeyLayoutData); + if (uchr == nil) { return; } const UCKeyboardLayout *keyboardLayout = (const UCKeyboardLayout*)CFDataGetBytePtr(uchr); // Carbon modifiers should be used instead of NSEvent modifiers UInt32 modifierKeyState = (GetCurrentEventKeyModifiers() >> 8) & 0xFF; @@ -563,18 +564,18 @@ NSUInteger JavaModifiersToNsKeyModifiers(jint javaModifiers, BOOL isExtMods) const struct _nsKeyToJavaModifier* cur; for (cur = nsKeyToJavaModifierTable; cur->nsMask != 0; ++cur) { - jint mask = isExtMods? cur->javaExtMask : cur->javaMask; + jint mask = isExtMods? cur->javaExtMask : cur->javaMask; if ((mask & javaModifiers) != 0) { nsFlags |= cur->nsMask; } } // special case - jint mask = isExtMods? java_awt_event_InputEvent_ALT_GRAPH_DOWN_MASK : + jint mask = isExtMods? java_awt_event_InputEvent_ALT_GRAPH_DOWN_MASK : java_awt_event_InputEvent_ALT_GRAPH_MASK; if ((mask & javaModifiers) != 0) { - nsFlags |= NSAlternateKeyMask; + nsFlags |= NSAlternateKeyMask; } return nsFlags; diff --git a/jdk/src/macosx/native/sun/awt/AWTView.m b/jdk/src/macosx/native/sun/awt/AWTView.m index 2d271777321..79219e4c55d 100644 --- a/jdk/src/macosx/native/sun/awt/AWTView.m +++ b/jdk/src/macosx/native/sun/awt/AWTView.m @@ -279,7 +279,10 @@ AWT_ASSERT_APPKIT_THREAD; return; } - if (![self hasMarkedText] && fKeyEventsNeeded) { + NSString *eventCharacters = [event characters]; + BOOL isDeadKey = (eventCharacters != nil && [eventCharacters length] == 0); + + if ((![self hasMarkedText] && fKeyEventsNeeded) || isDeadKey) { [self deliverJavaKeyEventHelper: event]; } diff --git a/jdk/test/java/awt/event/KeyEvent/DeadKey/DeadKeyMacOSXInputText.java b/jdk/test/java/awt/event/KeyEvent/DeadKey/DeadKeyMacOSXInputText.java new file mode 100644 index 00000000000..3ae73c6e684 --- /dev/null +++ b/jdk/test/java/awt/event/KeyEvent/DeadKey/DeadKeyMacOSXInputText.java @@ -0,0 +1,131 @@ +/* + * Copyright (c) 2012, 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. + * + * 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. + */ + +/* + * @test + * @bug 7199180 + * @summary [macosx] Dead keys handling for input methods + * @author alexandr.scherbatiy area=awt.event + * @run main DeadKeyMacOSXInputText + */ +import java.awt.*; +import java.awt.event.*; +import java.awt.event.KeyEvent; +import javax.swing.JTextField; +import sun.awt.OSInfo; +import sun.awt.SunToolkit; + +public class DeadKeyMacOSXInputText { + + private static SunToolkit toolkit; + private static volatile int state = 0; + + public static void main(String[] args) throws Exception { + + if (OSInfo.getOSType() != OSInfo.OSType.MACOSX) { + return; + } + + toolkit = (SunToolkit) Toolkit.getDefaultToolkit(); + Robot robot = new Robot(); + robot.setAutoDelay(50); + + createAndShowGUI(); + + // Pressed keys: Alt + E + A + // Results: ALT + VK_DEAD_ACUTE + a with accute accent + robot.keyPress(KeyEvent.VK_ALT); + robot.keyPress(KeyEvent.VK_E); + robot.keyRelease(KeyEvent.VK_E); + robot.keyRelease(KeyEvent.VK_ALT); + + robot.keyPress(KeyEvent.VK_A); + robot.keyRelease(KeyEvent.VK_A); + toolkit.realSync(); + + if (state != 3) { + throw new RuntimeException("Wrong number of key events."); + } + } + + static void createAndShowGUI() { + Frame frame = new Frame(); + frame.setSize(300, 300); + Panel panel = new Panel(new BorderLayout()); + JTextField textField = new JTextField(); + textField.addKeyListener(new DeadKeyListener()); + panel.add(textField, BorderLayout.CENTER); + frame.add(panel); + frame.setVisible(true); + toolkit.realSync(); + + textField.requestFocusInWindow(); + toolkit.realSync(); + + } + + static class DeadKeyListener extends KeyAdapter { + + @Override + public void keyPressed(KeyEvent e) { + int keyCode = e.getKeyCode(); + char keyChar = e.getKeyChar(); + + switch (state) { + case 0: + if (keyCode != KeyEvent.VK_ALT) { + throw new RuntimeException("Alt is not pressed."); + } + state++; + break; + case 1: + if (keyCode != KeyEvent.VK_DEAD_ACUTE) { + throw new RuntimeException("Dead ACUTE is not pressed."); + } + if (keyChar != 0xB4) { + throw new RuntimeException("Pressed char is not dead acute."); + } + state++; + break; + } + } + + @Override + public void keyTyped(KeyEvent e) { + int keyCode = e.getKeyCode(); + char keyChar = e.getKeyChar(); + + if (state == 2) { + if (keyCode != 0) { + throw new RuntimeException("Key code should be undefined."); + } + if (keyChar != 0xE1) { + throw new RuntimeException("A char does not have ACCUTE accent"); + } + state++; + } else { + throw new RuntimeException("Wron number of keyTyped events."); + } + } + } +} From 0ea3bcf8a89a3a1d3a6765bddce1ae9889d5eb06 Mon Sep 17 00:00:00 2001 From: Leonid Romanov Date: Mon, 24 Sep 2012 15:25:17 +0400 Subject: [PATCH 4/9] 7124239: [macosx] sun.awt.SunToolkit.InfiniteLoop exception in realSync called from SwingTestHelper Reviewed-by: anthony --- jdk/src/macosx/native/sun/awt/LWCToolkit.m | 20 ++++++--- .../native/sun/osxapp/NSApplicationAWT.h | 4 ++ .../native/sun/osxapp/NSApplicationAWT.m | 42 +++++++++++++++++++ 3 files changed, 61 insertions(+), 5 deletions(-) diff --git a/jdk/src/macosx/native/sun/awt/LWCToolkit.m b/jdk/src/macosx/native/sun/awt/LWCToolkit.m index f9b17fca6b7..b58cfc0627d 100644 --- a/jdk/src/macosx/native/sun/awt/LWCToolkit.m +++ b/jdk/src/macosx/native/sun/awt/LWCToolkit.m @@ -33,6 +33,7 @@ #import "ThreadUtilities.h" #import "AWT_debug.h" #import "CSystemColors.h" +#import "NSApplicationAWT.h" #import "sun_lwawt_macosx_LWCToolkit.h" @@ -47,7 +48,7 @@ static long eventCount; return eventCount; } -+ (void) eventCountPlusPlus{ ++ (void) eventCountPlusPlus{ eventCount++; } @@ -79,7 +80,6 @@ static long eventCount; @end - /* * Class: sun_lwawt_macosx_LWCToolkit * Method: nativeSyncQueue @@ -90,12 +90,22 @@ JNIEXPORT jboolean JNICALL Java_sun_lwawt_macosx_LWCToolkit_nativeSyncQueue { int currentEventNum = [AWTToolkit getEventCount]; - [JNFRunLoop performOnMainThreadWaiting:YES withBlock:^(){}]; - + NSApplication* sharedApp = [NSApplication sharedApplication]; + if ([sharedApp isKindOfClass:[NSApplicationAWT class]]) { + NSApplicationAWT* theApp = (NSApplicationAWT*)sharedApp; + [theApp postDummyEvent]; + [theApp waitForDummyEvent]; + } else { + // could happen if we are embedded inside SWT application, + // in this case just spin a single empty block through + // the event loop to give it a chance to process pending events + [JNFRunLoop performOnMainThreadWaiting:YES withBlock:^(){}]; + } + if (([AWTToolkit getEventCount] - currentEventNum) != 0) { return JNI_TRUE; } - + return JNI_FALSE; } diff --git a/jdk/src/macosx/native/sun/osxapp/NSApplicationAWT.h b/jdk/src/macosx/native/sun/osxapp/NSApplicationAWT.h index 3f4bc0ca749..f8db314a314 100644 --- a/jdk/src/macosx/native/sun/osxapp/NSApplicationAWT.h +++ b/jdk/src/macosx/native/sun/osxapp/NSApplicationAWT.h @@ -29,11 +29,15 @@ @interface NSApplicationAWT : NSApplication { NSString *fApplicationName; NSWindow *eventTransparentWindow; + NSTimeInterval dummyEventTimestamp; + NSConditionLock* seenDummyEventLock; } - (void) finishLaunching; - (void) registerWithProcessManager; - (void) setDockIconWithEnv:(JNIEnv *)env; +- (void) postDummyEvent; +- (void) waitForDummyEvent; + (void) runAWTLoopWithApp:(NSApplication*)app; diff --git a/jdk/src/macosx/native/sun/osxapp/NSApplicationAWT.m b/jdk/src/macosx/native/sun/osxapp/NSApplicationAWT.m index bc18e00f1fc..bc1efa35a7c 100644 --- a/jdk/src/macosx/native/sun/osxapp/NSApplicationAWT.m +++ b/jdk/src/macosx/native/sun/osxapp/NSApplicationAWT.m @@ -52,6 +52,9 @@ BOOL postEventDuringEventSynthesis = NO; AWT_ASSERT_APPKIT_THREAD; fApplicationName = nil; + dummyEventTimestamp = 0.0; + seenDummyEventLock = nil; + // NSApplication will call _RegisterApplication with the application's bundle, but there may not be one. // So, we need to call it ourselves to ensure the app is set up properly. @@ -328,6 +331,45 @@ AWT_ASSERT_APPKIT_THREAD; return event; } +// NSTimeInterval has microseconds precision +#define TS_EQUAL(ts1, ts2) (fabs((ts1) - (ts2)) < 1e-6) + +- (void)sendEvent:(NSEvent *)event +{ + if ([event type] == NSApplicationDefined && TS_EQUAL([event timestamp], dummyEventTimestamp)) { + [seenDummyEventLock lockWhenCondition:NO]; + [seenDummyEventLock unlockWithCondition:YES]; + } else { + [super sendEvent:event]; + } +} + +- (void)postDummyEvent { + seenDummyEventLock = [[NSConditionLock alloc] initWithCondition:NO]; + dummyEventTimestamp = [NSProcessInfo processInfo].systemUptime; + + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + NSEvent* event = [NSEvent otherEventWithType: NSApplicationDefined + location: NSMakePoint(0,0) + modifierFlags: 0 + timestamp: dummyEventTimestamp + windowNumber: 0 + context: nil + subtype: 0 + data1: 0 + data2: 0]; + [NSApp postEvent: event atStart: NO]; + [pool drain]; +} + +- (void)waitForDummyEvent { + [seenDummyEventLock lockWhenCondition:YES]; + [seenDummyEventLock unlock]; + [seenDummyEventLock release]; + + seenDummyEventLock = nil; +} + @end From 914bc61d4be0c1907ef57908623c20e957f03cef Mon Sep 17 00:00:00 2001 From: Leonid Romanov Date: Mon, 24 Sep 2012 18:24:30 +0400 Subject: [PATCH 5/9] 7179349: [macosx] Java processes on Mac should not use default Apple icon Reviewed-by: anthony --- jdk/make/sun/osxapp/Makefile | 38 ++++++++++++- jdk/make/sun/osxapp/ToBin.java | 50 ++++++++++++++++++ .../native/sun/osxapp/NSApplicationAWT.m | 30 ++++++----- .../sun/osxapp/resource/icons/JavaApp.icns | Bin 0 -> 85755 bytes 4 files changed, 102 insertions(+), 16 deletions(-) create mode 100644 jdk/make/sun/osxapp/ToBin.java create mode 100644 jdk/src/macosx/native/sun/osxapp/resource/icons/JavaApp.icns diff --git a/jdk/make/sun/osxapp/Makefile b/jdk/make/sun/osxapp/Makefile index 7c47edd2d16..cb90f3eee61 100644 --- a/jdk/make/sun/osxapp/Makefile +++ b/jdk/make/sun/osxapp/Makefile @@ -28,6 +28,11 @@ LIBRARY = osxapp PRODUCT = sun include $(BUILDDIR)/common/Defs.gmk +GEN_DIR=$(GENSRCDIR)/sun/osxapp +ICON_DATA = $(GEN_DIR)/AWTIconData.h + +CLASSES_INIT += $(ICON_DATA) + # # Files # @@ -63,6 +68,7 @@ OTHER_LDLIBS += \ -framework QuartzCore CPPFLAGS += \ + -I$(GEN_DIR) \ $(call NativeSrcDirList,-I,/native/sun/osxapp) @@ -70,6 +76,34 @@ ifeq ($(MILESTONE), internal) CPPFLAGS += -DINTERNAL_BUILD endif -clean clobber:: +TEMPDIR_CLASSES = $(TEMPDIR)/classes -.PHONY: +$(TEMPDIR_CLASSES)/sun/osxapp/ToBin.class: ToBin.java + @$(prep-target) + $(BOOT_JAVAC_CMD) -d $(TEMPDIR_CLASSES) $< + +ifdef OPENJDK + ICONS_PATH_PREFIX=$(PLATFORM_SRC_MACOS) +else + ICONS_PATH_PREFIX=$(CLOSED_SRC)/macosx +endif + +generated.clean: + $(RM) -r $(GEN_DIR)/*.h + +ICONPATH=$(ICONS_PATH_PREFIX)/native/sun/osxapp/resource/icons +ICON = $(ICONPATH)/JavaApp.icns + +$(GEN_DIR)/AWTIconData.h: $(TEMPDIR_CLASSES)/sun/osxapp/ToBin.class $(ICON) + $(prep-target) + $(RM) $(ICON_DATA) + $(ECHO) "static unsigned char sAWTIconData[] = { " >> $(ICON_DATA); \ + $(CAT) $(ICON) | \ + $(BOOT_JAVA_CMD) -cp $(TEMPDIR_CLASSES) -Djava.awt.headless=true \ + sun.osxapp.ToBin >> $(ICON_DATA); \ + $(ECHO) "};" >> $(ICON_DATA); + + +clean clobber:: generated.clean + +.PHONY: generated.clean diff --git a/jdk/make/sun/osxapp/ToBin.java b/jdk/make/sun/osxapp/ToBin.java new file mode 100644 index 00000000000..9fdcba32b79 --- /dev/null +++ b/jdk/make/sun/osxapp/ToBin.java @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2012, 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.osxapp; + +import java.io.*; + +public class ToBin { + public static void main(String[] args) throws Exception { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + int nRead; + byte[] data = new byte[4096]; + + while ((nRead = System.in.read(data, 0, data.length)) != -1) { + baos.write(data, 0, nRead); + } + + baos.flush(); + + byte[] buf = baos.toByteArray(); + for (int i = 0; i < buf.length; i++) { + System.out.print(String.format("0x%1$02X", buf[i]) + ", "); + if (i % 20 == 0) { + System.out.println(); + } + } + } +} diff --git a/jdk/src/macosx/native/sun/osxapp/NSApplicationAWT.m b/jdk/src/macosx/native/sun/osxapp/NSApplicationAWT.m index bc1efa35a7c..5c3c5f3addb 100644 --- a/jdk/src/macosx/native/sun/osxapp/NSApplicationAWT.m +++ b/jdk/src/macosx/native/sun/osxapp/NSApplicationAWT.m @@ -31,6 +31,7 @@ #import "PropertiesUtilities.h" #import "ThreadUtilities.h" #import "QueuingApplicationDelegate.h" +#import "AWTIconData.h" static BOOL sUsingDefaultNIB = YES; @@ -258,25 +259,26 @@ AWT_ASSERT_APPKIT_THREAD; theIconPath = [PropertiesUtilities javaSystemPropertyForKey:@"apple.awt.application.icon" withEnv:env]; } - // If the icon file wasn't specified as an argument and we need to get an icon - // we'll use the generic java app icon. - NSString *defaultIconPath = [NSString stringWithFormat:@"%@%@", SHARED_FRAMEWORK_BUNDLE, @"/Resources/GenericApp.icns"]; - if (theIconPath == nil) { + // Use the path specified to get the icon image + NSImage* iconImage = nil; + if (theIconPath != nil) { + iconImage = [[NSImage alloc] initWithContentsOfFile:theIconPath]; + } + + // If no icon file was specified or we failed to get the icon image + // and there is no bundle's icon, then use the default icon + if (iconImage == nil) { NSString* bundleIcon = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleIconFile"]; if (bundleIcon == nil) { - theIconPath = defaultIconPath; + NSData* iconData; + iconData = [[NSData alloc] initWithBytesNoCopy: sAWTIconData length: sizeof(sAWTIconData) freeWhenDone: NO]; + iconImage = [[NSImage alloc] initWithData: iconData]; + [iconData release]; } } - // Set up the dock icon if we have an icon name. - if (theIconPath != nil) { - NSImage *iconImage = [[NSImage alloc] initWithContentsOfFile:theIconPath]; - - // If we failed for some reason fall back to the default icon. - if (iconImage == nil) { - iconImage = [[NSImage alloc] initWithContentsOfFile:defaultIconPath]; - } - + // Set up the dock icon if we have an icon image. + if (iconImage != nil) { [NSApp setApplicationIconImage:iconImage]; [iconImage release]; } diff --git a/jdk/src/macosx/native/sun/osxapp/resource/icons/JavaApp.icns b/jdk/src/macosx/native/sun/osxapp/resource/icons/JavaApp.icns new file mode 100644 index 0000000000000000000000000000000000000000..fc60195cd0e919d1e184c2ce198ed59b3d487efa GIT binary patch literal 85755 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%0DQAm)==&;n3hv5iU45*k#%B zbWiR%aecoj36vyCS(-+R)3Y8ju8wzqy~fy_L$Y1^{b-*2?+alz>`1j$y9^>lKZ;~Y zXLd>tHVh0{+}25mfAHWg`EqNGfVH1(yz)<=JiMHS0rg%LMWf@uw2~hyD~-9GnTNe5 z4f8jC=xsi;y^|kN1NkA>+kq!+f0P zY%b2qgsHtGvz0sd-`Owgzrnyq^k9d=DJpy?eFKxWqYyg$w#_Ot$rxy53u3;~y#MrlDN` zXuV`Xc1HcwBfHA3c0b?HA==uCMdMD&|CJ5yW1dxq&e!^#L2{SxoiD_bq@-Zau&Q}{ zhl!68YD&|}A*Id*il2T$Sh+|G1$!B*t2-HYKL|p<(V_zv@~hH2o$40G8f%S;8X<>3 z$|&K}3)dmpO-fG^i;QmFQAGaCTY{4XZuWfq^Y`b`8YgB@CkpHc@2$|DckGvVxKV>} zb5oXoD2s)#`8}}MtC@SV^m?Yr$`L{AYHwdLGYf}y&Z2jN1D zsMgf}ebcm1t{@=(#;nI0M!|Qb4q9h+iptA#WX8qy&8slOi==w|rkr`%)w?lY-j(E) zdO)KZ?Wlbb?yr9K$lvvbd>H&-w(G_+tEPG(ge3QVBq%U=T%z}!s!V_l3dr#KxcYnN zOii;BDM7x6-M>b*QT!(qgo+ zzWxL{y8(*UMtc8IBEH#Sp6O|PPEL+T3WR{W@RcQS-! zu9)SYXEHl8^wfjZxnk9>bBW3ng8!|~QUibnU*s1Y&9ty?Z&&tX;%z%E$Zu)rYFDk@ z;cHnJ9)=4IBZa|l>Ah+sL-z>?&PTVax2hszOZ2eM)hDK;cX+D;Ap&-sL zX4k@mI0QQOX}7c+n%tmZa(yrNm+dhXa-tf)=RwOIBXs=N9zM5{vya8SK@$>@HuSRP z;~K@+*KR!bT+;DCf=ZykF<8=r09Qa^bBvQ2DUQoKF0hS@c zR>==Lg8p0T%Go324saWiwt6fC2`));Wkc`EoVyrtu%U`+g#CCXLPxC-^l#E}b5|0Y zr!TZa9hFz{hh=R>gLlzg(NL_T!|>vmXm71nkbJ&YOCsGBdVd9jSKvdTSp3`s9mz;N z3y%kgSZLoJ+lGg62C8fOhsDlGz|I_R<;zlZHkAsQO*&O*7*bJeKRPvf8<@!w0p(LlfU2^F;NVUqf#O4)HLA`;k%&nVFzstt2b0&zV>gL^tF)`y4+z;#}hyo8g%4i-b zz~t|jkho~+)(u1qOj1YnwF`eLImgZL?Rx1w9U8+gN4#?23%R)rg5S}o@{iB2%C>;} zfj%Uq0Xy1T1+YVFvYN&F8#t)nr2DPudS715=VL^HUKxVOD*~ZHzG7nQ(_Oa7?o6go zc3gD8&-KUNE^-VEpfTHQOaWy9DOEM%8}vwo$u!EMW&mpf0+z<;^!?5q85k?D*N~Q+ zZNhZPU&&pB1Dtd%y+kvmJVR5{(8C5N4^Fv6@gKCnQ_Hq>C52@6XgWFNG6r?VT|4g5 zC+ZjYyZ|13q!H2u)(Iehg}=w$1`YidbNs_Ne4tnYzq%OBbpYBjeQ7QBJtGT*O4pUv znv5`!ym=v*mn*>9J(6u-6tc#4v2xZKlSEKh39A1fDH;;fpN^q`H=2nu4H>*gaJW8SZF*{c2(Y3 z2D}q21jev6n018lE_B=%la`jQQBKbLd8x{a1+dq+&rmtuTRk#FSYoh3|BZg=lLGUV)qjEp_OIGf_%Qq zGZBzIt8>190sj=$0%7+k|Mk(1Al(18zaBn0xSUd9#$cwo4>~Ze>4Goem z0Bnzs18eX;x#Y_4PP}Po959a#*CPJ`aE`re5Fk@Ggmt0qy??} z6QBXlJm&3Yze&BMI5lyqp0s7~Qck^;h6BDs_t0yXhgagS*)R~x=L$^NwgbMa0nXSr zlMFZ8@Mt%ortkU~IPI>>mQMyiY*d%xUhTMWFo!@O`1toLfo$)?V&%B`em(|P4vrTU zoE?y~EbSL~KIYx)&0t3;gtEEmr!_nL#8D56JInwizR~!Wrf1XJw{#W7%OmKnd{VEd zcMR;cviVadHI5IakQ;ok+BhXSS>~2V5DPNY85PWszcr({`P@0)WFx6%YRMOs<3WCh z6F5~^E^e82Kmx&?nTNi}PXMEdy@HAfmz=vgvYd^yW(}fGb+Zr+4e!^p)Ij8P_q)MY zNQn$N__$qxC*T^bU1jc62<95gm|)+H9acxbHc;?|oMnV93mwoy_dWZUY0K zsU>C(mQ$&79>9d+Y;A3qIg)jb!9M)@=ki{HMHwrgQKmyM2KI9!Zv>V7Du&3y_mvee zqWJSC;9Udn(H^kk-+afJu?$SCYwN3`#RfQ7caan3h7vE%AZDyd;W~KOH`*>$W!!iu zk1kuZU*b&P;jBk0VcY$>S-Rg8YxPeg|!pf){G<&n(PUq`o8X++Z!^PEH z192bfyTqiaeG_Jl)q*u19^SMIo(_-qJO!|Dc(^5}0EU~3>uGqwx>+>B$;DMEDv1EF zAs=pp9%Sh<2p*3=kL5uR0(Cq8;{s$1oc5IY*CEy}>0cLNL4ZACvjgfUy|htHrOipnxAFmUGC^2MG=#B6bHIAG%)^)xzn9JdKwg)B8U z_2kb0Z3Ng?z7G}#W&zYTdqJuqS3UTIuzGX96e1xZF@=yUJimcf&{j$44+0f#>FJ&* zZK(P>{JzCthfeu&au49JPSEE2DB8@g?8iBnpf+bv41j>5Y|)bzwvCzQ2`Wpps&E@p zh61Vdw~a9RCmsS%h?Wn+pOu@XxSzKF_EbYeL}UZw^=xq+vx9L2s>TJUq@ZHtp>m8w_f@Fvz;4_#l`JIr(n5#Nt5NNR|445Gm82a0dtUO$?di_vtSUXZs ztbc7su>b5gGc^VfmLnrKkXp@wo~K4kXjVUjF&$aDJb+8xaDe6Yi2BBc2>2T*oe%|S zSauLQ>*(pld(sXe*hAY{rKNx6Rg9lOen)g(eAR}h^inx%`(mT5ef zom}yu{4J?aCdl(4M)gh#2C|M~y*{UIJ4(38lfgH@E@C{+qRZE~=+kBEJe`T%((y<_ z3QGnd$bip}5k647cqhewLjMn(!xEktrETBrcn71(lu>T7TO5XvMDZwTim7-1yhbZtuU7T Date: Mon, 24 Sep 2012 21:33:41 +0400 Subject: [PATCH 6/9] 7160627: [macosx] TextArea has wrong initial size 7124213: [macosx] pack() does ignore size of a component; doesn't on the other platforms Reviewed-by: anthony, art --- .../classes/sun/lwawt/LWCanvasPeer.java | 27 +++++-- .../classes/sun/lwawt/LWCheckboxPeer.java | 7 +- .../classes/sun/lwawt/LWComponentPeer.java | 77 ++++++++++-------- .../classes/sun/lwawt/LWContainerPeer.java | 4 +- .../macosx/classes/sun/lwawt/LWLabelPeer.java | 33 +------- .../macosx/classes/sun/lwawt/LWListPeer.java | 78 +++++++++++++------ .../macosx/classes/sun/lwawt/LWPanelPeer.java | 6 -- .../classes/sun/lwawt/LWScrollBarPeer.java | 8 -- .../classes/sun/lwawt/LWTextAreaPeer.java | 49 +++++++++--- .../sun/lwawt/LWTextComponentPeer.java | 18 ++--- .../classes/sun/lwawt/LWTextFieldPeer.java | 17 +--- .../macosx/classes/sun/lwawt/LWToolkit.java | 2 +- .../ScrollPanePreferredSize.java | 65 ++++++++++++++++ .../TextAreaTwicePack/TextAreaTwicePack.java | 66 ++++++++++++++++ 14 files changed, 310 insertions(+), 147 deletions(-) create mode 100644 jdk/test/java/awt/ScrollPane/ScrollPanePreferredSize/ScrollPanePreferredSize.java create mode 100644 jdk/test/java/awt/TextArea/TextAreaTwicePack/TextAreaTwicePack.java diff --git a/jdk/src/macosx/classes/sun/lwawt/LWCanvasPeer.java b/jdk/src/macosx/classes/sun/lwawt/LWCanvasPeer.java index e5d836288a5..f61b978d230 100644 --- a/jdk/src/macosx/classes/sun/lwawt/LWCanvasPeer.java +++ b/jdk/src/macosx/classes/sun/lwawt/LWCanvasPeer.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, 2012, 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 @@ -26,26 +26,27 @@ package sun.lwawt; +import java.awt.AWTException; import java.awt.BufferCapabilities; -import java.awt.Canvas; import java.awt.Component; +import java.awt.Dimension; import java.awt.GraphicsConfiguration; import java.awt.Image; import java.awt.peer.CanvasPeer; import javax.swing.JComponent; -final class LWCanvasPeer extends LWComponentPeer - implements CanvasPeer { +class LWCanvasPeer + extends LWComponentPeer implements CanvasPeer { - LWCanvasPeer(final Canvas target, PlatformComponent platformComponent) { + LWCanvasPeer(final T target, final PlatformComponent platformComponent) { super(target, platformComponent); } - // ---- PEER METHODS ---- // @Override - public void createBuffers(int numBuffers, BufferCapabilities caps) { + public void createBuffers(int numBuffers, BufferCapabilities caps) + throws AWTException { // TODO } @@ -67,10 +68,20 @@ final class LWCanvasPeer extends LWComponentPeer } @Override - public GraphicsConfiguration getAppropriateGraphicsConfiguration( + public final GraphicsConfiguration getAppropriateGraphicsConfiguration( GraphicsConfiguration gc) { // TODO return gc; } + + @Override + public final Dimension getPreferredSize() { + return getMinimumSize(); + } + + @Override + public final Dimension getMinimumSize() { + return getBounds().getSize(); + } } diff --git a/jdk/src/macosx/classes/sun/lwawt/LWCheckboxPeer.java b/jdk/src/macosx/classes/sun/lwawt/LWCheckboxPeer.java index 69bce89adae..de1c378b860 100644 --- a/jdk/src/macosx/classes/sun/lwawt/LWCheckboxPeer.java +++ b/jdk/src/macosx/classes/sun/lwawt/LWCheckboxPeer.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, 2012, 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 @@ -185,6 +185,11 @@ final class LWCheckboxPeer rb.setBounds(0, 0, w, h); } + @Override + public Dimension getPreferredSize() { + return getCurrentButton().getPreferredSize(); + } + @Override @Transient public Dimension getMinimumSize() { diff --git a/jdk/src/macosx/classes/sun/lwawt/LWComponentPeer.java b/jdk/src/macosx/classes/sun/lwawt/LWComponentPeer.java index 1704b3bdd25..db6d2dd4430 100644 --- a/jdk/src/macosx/classes/sun/lwawt/LWComponentPeer.java +++ b/jdk/src/macosx/classes/sun/lwawt/LWComponentPeer.java @@ -123,7 +123,7 @@ public abstract class LWComponentPeer // private volatile boolean paintPending; private volatile boolean isLayouting; - private D delegate = null; + private final D delegate; private Container delegateContainer; private Component delegateDropTarget; private final Object dropTargetLock = new Object(); @@ -133,6 +133,11 @@ public abstract class LWComponentPeer private final PlatformComponent platformComponent; + /** + * Character with reasonable value between the minimum width and maximum. + */ + static final char WIDE_CHAR = '0'; + private final class DelegateContainer extends Container { { enableEvents(0xFFFFFFFF); @@ -267,9 +272,7 @@ public abstract class LWComponentPeer } protected final D getDelegate() { - synchronized (getStateLock()) { - return delegate; - } + return delegate; } protected Component getDelegateFocusOwner() { @@ -698,26 +701,23 @@ public abstract class LWComponentPeer } @Override - public FontMetrics getFontMetrics(Font f) { + public FontMetrics getFontMetrics(final Font f) { // Borrow the metrics from the top-level window // return getWindowPeer().getFontMetrics(f); // Obtain the metrics from the offscreen window where this peer is // mostly drawn to. // TODO: check for "use platform metrics" settings - Graphics g = getWindowPeer().getGraphics(); - try { - if (g != null) { + final Graphics g = getOnscreenGraphics(); + if (g != null) { + try { return g.getFontMetrics(f); - } else { - synchronized (getDelegateLock()) { - return delegateContainer.getFontMetrics(f); - } - } - } finally { - if (g != null) { + } finally { g.dispose(); } } + synchronized (getDelegateLock()) { + return delegateContainer.getFontMetrics(f); + } } @Override @@ -847,31 +847,46 @@ public abstract class LWComponentPeer } /** - * Should be overridden in subclasses to forward the request - * to the Swing helper component, if required. + * Determines the preferred size of the component. By default forwards the + * request to the Swing helper component. Should be overridden in subclasses + * if required. */ @Override public Dimension getPreferredSize() { - // It looks like a default implementation for all toolkits - return getMinimumSize(); + final Dimension size; + synchronized (getDelegateLock()) { + size = getDelegate().getPreferredSize(); + } + return validateSize(size); } - /* - * Should be overridden in subclasses to forward the request - * to the Swing helper component. + /** + * Determines the minimum size of the component. By default forwards the + * request to the Swing helper component. Should be overridden in subclasses + * if required. */ @Override public Dimension getMinimumSize() { - D delegate = getDelegate(); - - if (delegate == null) { - // Is it a correct default value? - return getBounds().getSize(); - } else { - synchronized (getDelegateLock()) { - return delegate.getMinimumSize(); - } + final Dimension size; + synchronized (getDelegateLock()) { + size = getDelegate().getMinimumSize(); } + return validateSize(size); + } + + /** + * In some situations delegates can return empty minimum/preferred size. + * (For example: empty JLabel, etc), but awt components never should be + * empty. In the XPeers or WPeers we use some magic constants, but here we + * try to use something more useful, + */ + private Dimension validateSize(final Dimension size) { + if (size.width == 0 || size.height == 0) { + final FontMetrics fm = getFontMetrics(getFont()); + size.width = fm.charWidth(WIDE_CHAR); + size.height = fm.getHeight(); + } + return size; } @Override diff --git a/jdk/src/macosx/classes/sun/lwawt/LWContainerPeer.java b/jdk/src/macosx/classes/sun/lwawt/LWContainerPeer.java index dab0518754e..c213664b8ce 100644 --- a/jdk/src/macosx/classes/sun/lwawt/LWContainerPeer.java +++ b/jdk/src/macosx/classes/sun/lwawt/LWContainerPeer.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, 2012, 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 @@ -41,7 +41,7 @@ import java.util.List; import javax.swing.JComponent; abstract class LWContainerPeer - extends LWComponentPeer + extends LWCanvasPeer implements ContainerPeer { // List of child peers sorted by z-order from bottom-most diff --git a/jdk/src/macosx/classes/sun/lwawt/LWLabelPeer.java b/jdk/src/macosx/classes/sun/lwawt/LWLabelPeer.java index f8c764364cc..19743da74a3 100644 --- a/jdk/src/macosx/classes/sun/lwawt/LWLabelPeer.java +++ b/jdk/src/macosx/classes/sun/lwawt/LWLabelPeer.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, 2012, 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 @@ -26,37 +26,26 @@ package sun.lwawt; -import java.awt.Dimension; -import java.awt.FontMetrics; import java.awt.Label; import java.awt.peer.LabelPeer; import javax.swing.JLabel; import javax.swing.SwingConstants; -import javax.tools.annotation.GenerateNativeHeader; - /** * Lightweight implementation of {@link LabelPeer}. Delegates most of the work * to the {@link JLabel}. */ -/* No native methods here, but the constants are needed in the supporting JNI code */ -@GenerateNativeHeader final class LWLabelPeer extends LWComponentPeer implements LabelPeer { - private static final int TEXT_XPAD = 5; - private static final int TEXT_YPAD = 1; - LWLabelPeer(final Label target, final PlatformComponent platformComponent) { super(target, platformComponent); } @Override protected JLabel createDelegate() { - final JLabel label = new JLabel(); - label.setVerticalAlignment(SwingConstants.TOP); - return label; + return new JLabel(); } @Override @@ -80,24 +69,6 @@ final class LWLabelPeer extends LWComponentPeer } } - @Override - public Dimension getMinimumSize() { - int w = TEXT_XPAD; - int h = TEXT_YPAD; - final FontMetrics fm = getFontMetrics(getFont()); - if (fm != null) { - final String text; - synchronized (getDelegateLock()) { - text = getDelegate().getText(); - } - if (text != null) { - w += fm.stringWidth(text); - } - h += fm.getHeight(); - } - return new Dimension(w, h); - } - /** * Converts {@code Label} alignment constant to the {@code JLabel} constant. * If wrong Label alignment provided returns default alignment. diff --git a/jdk/src/macosx/classes/sun/lwawt/LWListPeer.java b/jdk/src/macosx/classes/sun/lwawt/LWListPeer.java index 1e6154fd64f..5edb8e16cd0 100644 --- a/jdk/src/macosx/classes/sun/lwawt/LWListPeer.java +++ b/jdk/src/macosx/classes/sun/lwawt/LWListPeer.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, 2012, 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 @@ -32,10 +32,22 @@ import java.awt.event.*; import java.awt.peer.ListPeer; import java.util.Arrays; -final class LWListPeer - extends LWComponentPeer +/** + * Lightweight implementation of {@link ListPeer}. + */ +final class LWListPeer extends LWComponentPeer implements ListPeer { + /** + * The default number of visible rows. + */ + private static final int DEFAULT_VISIBLE_ROWS = 4; // From java.awt.List, + + /** + * This text is used for cell bounds calculation. + */ + private static final String TEXT = "0123456789abcde"; + LWListPeer(final List target, final PlatformComponent platformComponent) { super(target, platformComponent); if (!getTarget().isBackgroundSet()) { @@ -134,6 +146,16 @@ final class LWListPeer } } + @Override + public Dimension getPreferredSize() { + return getMinimumSize(); + } + + @Override + public Dimension getMinimumSize() { + return getMinimumSize(DEFAULT_VISIBLE_ROWS); + } + @Override public Dimension getPreferredSize(final int rows) { return getMinimumSize(rows); @@ -142,18 +164,28 @@ final class LWListPeer @Override public Dimension getMinimumSize(final int rows) { synchronized (getDelegateLock()) { - final int margin = 2; - final int space = 1; - - // TODO: count ScrollPane's scrolling elements if any. - final FontMetrics fm = getFontMetrics(getFont()); - final int itemHeight = (fm.getHeight() - fm.getLeading()) + (2 * space); - - return new Dimension(20 + (fm == null ? 10 * 15 : fm.stringWidth("0123456789abcde")), - (fm == null ? 10 : itemHeight) * rows + (2 * margin)); + final Dimension size = getCellSize(); + size.height *= rows; + // Always take vertical scrollbar into account. + final JScrollBar vbar = getDelegate().getVerticalScrollBar(); + size.width += vbar != null ? vbar.getMinimumSize().width : 0; + // JScrollPane and JList insets + final Insets pi = getDelegate().getInsets(); + final Insets vi = getDelegate().getView().getInsets(); + size.width += pi.left + pi.right + vi.left + vi.right; + size.height += pi.top + pi.bottom + vi.top + vi.bottom; + return size; } } + private Dimension getCellSize() { + final JList jList = getDelegate().getView(); + final ListCellRenderer cr = jList.getCellRenderer(); + final Component cell = cr.getListCellRendererComponent(jList, TEXT, 0, + false, false); + return cell.getPreferredSize(); + } + private void revalidate() { synchronized (getDelegateLock()) { getDelegate().getView().invalidate(); @@ -165,10 +197,10 @@ final class LWListPeer private boolean skipStateChangedEvent; - private DefaultListModel model = - new DefaultListModel() { + private final DefaultListModel model = + new DefaultListModel() { @Override - public void add(final int index, final Object element) { + public void add(final int index, final String element) { if (index == -1) { addElement(element); } else { @@ -181,7 +213,7 @@ final class LWListPeer ScrollableJList() { getViewport().setScrollMode(JViewport.SIMPLE_SCROLL_MODE); - final JList list = new JListDelegate(); + final JList list = new JListDelegate(); list.addListSelectionListener(this); getViewport().setView(list); @@ -223,11 +255,11 @@ final class LWListPeer } } - public JList getView() { - return (JList) getViewport().getView(); + public JList getView() { + return (JList) getViewport().getView(); } - public DefaultListModel getModel() { + public DefaultListModel getModel() { return model; } @@ -254,7 +286,7 @@ final class LWListPeer } } - private final class JListDelegate extends JList { + private final class JListDelegate extends JList { JListDelegate() { super(ScrollableJList.this.model); @@ -272,7 +304,7 @@ final class LWListPeer final int index = locationToIndex(e.getPoint()); if (0 <= index && index < getModel().getSize()) { LWListPeer.this.postEvent(new ActionEvent(getTarget(), ActionEvent.ACTION_PERFORMED, - getModel().getElementAt(index).toString(), e.getWhen(), e.getModifiers())); + getModel().getElementAt(index), e.getWhen(), e.getModifiers())); } } } @@ -281,10 +313,10 @@ final class LWListPeer protected void processKeyEvent(final KeyEvent e) { super.processKeyEvent(e); if (e.getID() == KeyEvent.KEY_PRESSED && e.getKeyCode() == KeyEvent.VK_ENTER) { - final Object selectedValue = getSelectedValue(); + final String selectedValue = getSelectedValue(); if(selectedValue != null){ LWListPeer.this.postEvent(new ActionEvent(getTarget(), ActionEvent.ACTION_PERFORMED, - selectedValue.toString(), e.getWhen(), e.getModifiers())); + selectedValue, e.getWhen(), e.getModifiers())); } } } diff --git a/jdk/src/macosx/classes/sun/lwawt/LWPanelPeer.java b/jdk/src/macosx/classes/sun/lwawt/LWPanelPeer.java index 230cb89118e..6b1f48b08ad 100644 --- a/jdk/src/macosx/classes/sun/lwawt/LWPanelPeer.java +++ b/jdk/src/macosx/classes/sun/lwawt/LWPanelPeer.java @@ -26,7 +26,6 @@ package sun.lwawt; -import java.awt.Dimension; import java.awt.Panel; import java.awt.peer.PanelPeer; @@ -43,9 +42,4 @@ final class LWPanelPeer extends LWContainerPeer public JPanel createDelegate() { return new JPanel(); } - - @Override - public Dimension getMinimumSize() { - return getBounds().getSize(); - } } diff --git a/jdk/src/macosx/classes/sun/lwawt/LWScrollBarPeer.java b/jdk/src/macosx/classes/sun/lwawt/LWScrollBarPeer.java index 688f9e96f16..dff48a4caa3 100644 --- a/jdk/src/macosx/classes/sun/lwawt/LWScrollBarPeer.java +++ b/jdk/src/macosx/classes/sun/lwawt/LWScrollBarPeer.java @@ -27,7 +27,6 @@ package sun.lwawt; import java.awt.Adjustable; -import java.awt.Dimension; import java.awt.Scrollbar; import java.awt.event.AdjustmentEvent; import java.awt.event.AdjustmentListener; @@ -93,13 +92,6 @@ final class LWScrollBarPeer extends LWComponentPeer } } - @Override - public Dimension getPreferredSize() { - synchronized (getDelegateLock()) { - return getDelegate().getPreferredSize(); - } - } - // Peer also registered as a listener for ComponentDelegate component @Override public void adjustmentValueChanged(final AdjustmentEvent e) { diff --git a/jdk/src/macosx/classes/sun/lwawt/LWTextAreaPeer.java b/jdk/src/macosx/classes/sun/lwawt/LWTextAreaPeer.java index 48467c19e2e..98032e20c29 100644 --- a/jdk/src/macosx/classes/sun/lwawt/LWTextAreaPeer.java +++ b/jdk/src/macosx/classes/sun/lwawt/LWTextAreaPeer.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, 2012, 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 @@ -29,6 +29,7 @@ package sun.lwawt; import java.awt.Component; import java.awt.Cursor; import java.awt.Dimension; +import java.awt.Insets; import java.awt.Point; import java.awt.TextArea; import java.awt.event.TextEvent; @@ -41,11 +42,22 @@ import javax.swing.ScrollPaneConstants; import javax.swing.text.Document; import javax.swing.text.JTextComponent; +/** + * Lightweight implementation of {@link TextAreaPeer}. Delegates most of the + * work to the {@link JTextArea} inside JScrollPane. + */ final class LWTextAreaPeer extends LWTextComponentPeer implements TextAreaPeer { + /** + * The default number of visible columns. + */ private static final int DEFAULT_COLUMNS = 60; + + /** + * The default number of visible rows. + */ private static final int DEFAULT_ROWS = 10; LWTextAreaPeer(final TextArea target, @@ -86,27 +98,42 @@ final class LWTextAreaPeer return getTextComponent(); } + @Override + public Dimension getPreferredSize() { + return getMinimumSize(); + } + @Override public Dimension getMinimumSize() { return getMinimumSize(DEFAULT_ROWS, DEFAULT_COLUMNS); } @Override - public Dimension getMinimumSize(final int rows, final int columns) { - return getPreferredSize(rows, columns); + public Dimension getPreferredSize(final int rows, final int columns) { + return getMinimumSize(rows, columns); } @Override - public Dimension getPreferredSize(final int rows, final int columns) { - final Dimension size = super.getPreferredSize(rows, columns); + public Dimension getMinimumSize(final int rows, final int columns) { + final Dimension size = super.getMinimumSize(rows, columns); synchronized (getDelegateLock()) { - final JScrollBar vbar = getDelegate().getVerticalScrollBar(); - final JScrollBar hbar = getDelegate().getHorizontalScrollBar(); - final int scrollbarW = vbar != null ? vbar.getWidth() : 0; - final int scrollbarH = hbar != null ? hbar.getHeight() : 0; - return new Dimension(size.width + scrollbarW, - size.height + scrollbarH); + // JScrollPane insets + final Insets pi = getDelegate().getInsets(); + size.width += pi.left + pi.right; + size.height += pi.top + pi.bottom; + // Take scrollbars into account. + final int vsbPolicy = getDelegate().getVerticalScrollBarPolicy(); + if (vsbPolicy == ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS) { + final JScrollBar vbar = getDelegate().getVerticalScrollBar(); + size.width += vbar != null ? vbar.getMinimumSize().width : 0; + } + final int hsbPolicy = getDelegate().getHorizontalScrollBarPolicy(); + if (hsbPolicy == ScrollPaneConstants.HORIZONTAL_SCROLLBAR_ALWAYS) { + final JScrollBar hbar = getDelegate().getHorizontalScrollBar(); + size.height += hbar != null ? hbar.getMinimumSize().height : 0; + } } + return size; } @Override diff --git a/jdk/src/macosx/classes/sun/lwawt/LWTextComponentPeer.java b/jdk/src/macosx/classes/sun/lwawt/LWTextComponentPeer.java index 9db975da915..aab0baf43e0 100644 --- a/jdk/src/macosx/classes/sun/lwawt/LWTextComponentPeer.java +++ b/jdk/src/macosx/classes/sun/lwawt/LWTextComponentPeer.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, 2012, 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 @@ -48,10 +48,7 @@ abstract class LWTextComponentPeer implements DocumentListener, TextComponentPeer, InputMethodListener { - /** - * Character with reasonable value between the minimum width and maximum. - */ - protected static final char WIDE_CHAR = 'w'; + private volatile boolean firstChangeSkipped; LWTextComponentPeer(final T target, @@ -95,18 +92,16 @@ abstract class LWTextComponentPeer implements TextFieldPeer, ActionListener { - private static final int DEFAULT_COLUMNS = 1; - LWTextFieldPeer(final TextField target, final PlatformComponent platformComponent) { super(target, platformComponent); @@ -83,17 +77,12 @@ final class LWTextFieldPeer @Override public Dimension getPreferredSize(final int columns) { - return getPreferredSize(1, columns); + return getMinimumSize(columns); } @Override public Dimension getMinimumSize(final int columns) { - return getPreferredSize(columns); - } - - @Override - public Dimension getMinimumSize() { - return getMinimumSize(DEFAULT_COLUMNS); + return getMinimumSize(1, columns); } @Override diff --git a/jdk/src/macosx/classes/sun/lwawt/LWToolkit.java b/jdk/src/macosx/classes/sun/lwawt/LWToolkit.java index 3c6ab83bcd6..cba6cec9c9f 100644 --- a/jdk/src/macosx/classes/sun/lwawt/LWToolkit.java +++ b/jdk/src/macosx/classes/sun/lwawt/LWToolkit.java @@ -310,7 +310,7 @@ public abstract class LWToolkit extends SunToolkit implements Runnable { @Override public CanvasPeer createCanvas(Canvas target) { PlatformComponent platformComponent = createPlatformComponent(); - LWCanvasPeer peer = new LWCanvasPeer(target, platformComponent); + LWCanvasPeer peer = new LWCanvasPeer<>(target, platformComponent); targetCreatedPeer(target, peer); peer.initialize(); return peer; diff --git a/jdk/test/java/awt/ScrollPane/ScrollPanePreferredSize/ScrollPanePreferredSize.java b/jdk/test/java/awt/ScrollPane/ScrollPanePreferredSize/ScrollPanePreferredSize.java new file mode 100644 index 00000000000..a8c3c822bc5 --- /dev/null +++ b/jdk/test/java/awt/ScrollPane/ScrollPanePreferredSize/ScrollPanePreferredSize.java @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2012, 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. + */ + +import java.awt.Dimension; +import java.awt.Frame; +import java.awt.ScrollPane; +import java.awt.Toolkit; + +import sun.awt.SunToolkit; + +/** + * @test + * @bug 7124213 + * @author Sergey Bylokhov + */ +public final class ScrollPanePreferredSize { + + public static void main(final String[] args) { + final Dimension expected = new Dimension(300, 300); + final Frame frame = new Frame(); + final ScrollPane sp = new ScrollPane(); + sp.setSize(expected); + frame.add(sp); + frame.pack(); + frame.setLocationRelativeTo(null); + frame.setVisible(true); + sleep(); + final Dimension size = frame.getSize(); + if (size.width < expected.width || size.height < expected.height) { + throw new RuntimeException( + "Expected size: >= " + expected + ", actual size: " + size); + } + frame.dispose(); + } + + private static void sleep() { + ((SunToolkit) Toolkit.getDefaultToolkit()).realSync(); + try { + Thread.sleep(500L); + } catch (InterruptedException ignored) { + } + } +} diff --git a/jdk/test/java/awt/TextArea/TextAreaTwicePack/TextAreaTwicePack.java b/jdk/test/java/awt/TextArea/TextAreaTwicePack/TextAreaTwicePack.java new file mode 100644 index 00000000000..edd4eded97f --- /dev/null +++ b/jdk/test/java/awt/TextArea/TextAreaTwicePack/TextAreaTwicePack.java @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2012, 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. + */ + +import java.awt.Dimension; +import java.awt.Frame; +import java.awt.TextArea; +import java.awt.Toolkit; + +import sun.awt.SunToolkit; + +/** + * @test + * @bug 7160627 + * @summary We shouldn't get different frame size when we call Frame.pack() + * twice. + * @author Sergey Bylokhov + */ +public final class TextAreaTwicePack { + + public static void main(final String[] args) { + final Frame frame = new Frame(); + final TextArea ta = new TextArea(); + frame.add(ta); + frame.pack(); + frame.setVisible(true); + sleep(); + final Dimension before = frame.getSize(); + frame.pack(); + final Dimension after = frame.getSize(); + if (!after.equals(before)) { + throw new RuntimeException( + "Expected size: " + before + ", actual size: " + after); + } + frame.dispose(); + } + + private static void sleep() { + ((SunToolkit) Toolkit.getDefaultToolkit()).realSync(); + try { + Thread.sleep(500L); + } catch (InterruptedException ignored) { + } + } +} From 8bb2411d345293847577f57c668836151782edd3 Mon Sep 17 00:00:00 2001 From: Alexander Scherbatiy Date: Wed, 26 Sep 2012 18:59:12 +0400 Subject: [PATCH 7/9] 7124515: [macosx] Test fail like 6366126 (ArrayIndexOutOfBoundException pressing ENTER after removing items) Reviewed-by: serb, anthony --- .../EmptyListEventTest.java | 158 ++++++++++++++++++ 1 file changed, 158 insertions(+) create mode 100644 jdk/test/java/awt/List/EmptyListEventTest/EmptyListEventTest.java diff --git a/jdk/test/java/awt/List/EmptyListEventTest/EmptyListEventTest.java b/jdk/test/java/awt/List/EmptyListEventTest/EmptyListEventTest.java new file mode 100644 index 00000000000..c5515afa6b2 --- /dev/null +++ b/jdk/test/java/awt/List/EmptyListEventTest/EmptyListEventTest.java @@ -0,0 +1,158 @@ +/* + * Copyright (c) 2008, 2012, 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. + * + * 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. + */ + +/* + * @test + * @bug 6366126 + * @summary List throws ArrayIndexOutOfBoundsException when pressing ENTER after removing all the items, Win32 + * @author Dmitry Cherepanov area=awt.list + * @run main EmptyListEventTest + */ +import java.awt.*; +import java.awt.event.*; +import javax.swing.JFrame; +import javax.swing.JPanel; +import javax.swing.SwingUtilities; +import sun.awt.SunToolkit; + +public class EmptyListEventTest { + + private static List list; + + public static void main(String[] args) throws Exception { + + SunToolkit toolkit = (SunToolkit) Toolkit.getDefaultToolkit(); + Robot robot = new Robot(); + robot.setAutoDelay(50); + + SwingUtilities.invokeAndWait(new Runnable() { + + @Override + public void run() { + createAndShowGUI(); + } + }); + + toolkit.realSync(); + + // press mouse -> ItemEvent + Point point = getClickPoint(); + robot.mouseMove(point.x, point.y); + robot.mousePress(InputEvent.BUTTON1_MASK); + robot.mouseRelease(InputEvent.BUTTON1_MASK); + + toolkit.realSync(); + + SwingUtilities.invokeAndWait(new Runnable() { + + @Override + public void run() { + list.requestFocusInWindow(); + } + }); + + toolkit.realSync(); + + if (KeyboardFocusManager.getCurrentKeyboardFocusManager().getFocusOwner() != list) { + throw new RuntimeException("Test failed - list isn't focus owner."); + } + + // press key ENTER -> ActionEvent + robot.keyPress(KeyEvent.VK_ENTER); + robot.keyRelease(KeyEvent.VK_ENTER); + toolkit.realSync(); + + // press key SPACE -> ItemEvent + robot.keyPress(KeyEvent.VK_SPACE); + robot.keyRelease(KeyEvent.VK_SPACE); + toolkit.realSync(); + + // mouse double click -> ActionEvent + robot.setAutoDelay(10); + robot.mousePress(InputEvent.BUTTON1_MASK); + robot.mouseRelease(InputEvent.BUTTON1_MASK); + robot.mousePress(InputEvent.BUTTON1_MASK); + robot.mouseRelease(InputEvent.BUTTON1_MASK); + toolkit.realSync(); + } + + private static Point getClickPoint() throws Exception { + final Point[] result = new Point[1]; + + SwingUtilities.invokeAndWait(new Runnable() { + + @Override + public void run() { + Point point = list.getLocationOnScreen(); + point.translate(list.getWidth() / 2, list.getHeight() / 2); + result[0] = point; + + } + }); + + return result[0]; + + + } + + private static void createAndShowGUI() { + JFrame frame = new JFrame(); + frame.setSize(200, 200); + frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + + JPanel panel = new JPanel(new BorderLayout()); + + frame.getToolkit().addAWTEventListener(new AWTEventListener() { + + public void eventDispatched(AWTEvent e) { + System.out.println(e); + } + }, AWTEvent.FOCUS_EVENT_MASK | AWTEvent.WINDOW_FOCUS_EVENT_MASK); + + + MyListener listener = new MyListener(); + + list = new List(4, true); + list.addActionListener(listener); + list.addItemListener(listener); + + panel.add(list); + + frame.getContentPane().add(panel); + frame.setVisible(true); + + } + + static class MyListener implements ActionListener, ItemListener { + + public void actionPerformed(ActionEvent ae) { + System.err.println(ae); + throw new RuntimeException("Test failed - list is empty so event is redundant"); + } + + public void itemStateChanged(ItemEvent ie) { + System.err.println(ie); + throw new RuntimeException("Test failed - list is empty so event is redundant"); + } + } +} From 01025ec99e8e44ced3f132c4d1c116a3e40ab8a2 Mon Sep 17 00:00:00 2001 From: Alexander Scherbatiy Date: Fri, 28 Sep 2012 14:54:04 +0400 Subject: [PATCH 8/9] 7197619: Using modifiers for the dead key detection on Windows Reviewed-by: bagiras, leonidr --- .../native/sun/windows/awt_Component.cpp | 50 +++++++++++++------ .../native/sun/windows/awt_Component.h | 4 +- 2 files changed, 36 insertions(+), 18 deletions(-) diff --git a/jdk/src/windows/native/sun/windows/awt_Component.cpp b/jdk/src/windows/native/sun/windows/awt_Component.cpp index d7b265c44e3..d6c4e974e0f 100644 --- a/jdk/src/windows/native/sun/windows/awt_Component.cpp +++ b/jdk/src/windows/native/sun/windows/awt_Component.cpp @@ -3144,7 +3144,8 @@ void AwtComponent::JavaKeyToWindowsKey(UINT javaKey, return; } -UINT AwtComponent::WindowsKeyToJavaKey(UINT windowsKey, UINT modifiers) +UINT AwtComponent::WindowsKeyToJavaKey(UINT windowsKey, UINT modifiers, UINT character, BOOL isDeadKey) + { // Handle the few cases where we need to take the modifier into // consideration for the Java VK code or where we have to take the keyboard @@ -3171,6 +3172,15 @@ UINT AwtComponent::WindowsKeyToJavaKey(UINT windowsKey, UINT modifiers) break; }; + // check dead key + if (isDeadKey) { + for (int i = 0; charToDeadVKTable[i].c != 0; i++) { + if (charToDeadVKTable[i].c == character) { + return charToDeadVKTable[i].javaKey; + } + } + } + // for the general case, use a bi-directional table for (int i = 0; keyMapTable[i].windowsKey != 0; i++) { if (keyMapTable[i].windowsKey == windowsKey) { @@ -3384,14 +3394,18 @@ AwtComponent::UpdateDynPrimaryKeymap(UINT wkey, UINT jkeyLegacy, jint keyLocatio } } -UINT AwtComponent::WindowsKeyToJavaChar(UINT wkey, UINT modifiers, TransOps ops) +UINT AwtComponent::WindowsKeyToJavaChar(UINT wkey, UINT modifiers, TransOps ops, BOOL &isDeadKey) { static Hashtable transTable("VKEY translations"); + static Hashtable deadKeyFlagTable("Dead Key Flags"); + isDeadKey = FALSE; // Try to translate using last saved translation if (ops == LOAD) { + void* deadKeyFlag = deadKeyFlagTable.remove(reinterpret_cast(static_cast(wkey))); void* value = transTable.remove(reinterpret_cast(static_cast(wkey))); if (value != NULL) { + isDeadKey = static_cast(reinterpret_cast(deadKeyFlag)); return static_cast(reinterpret_cast(value)); } } @@ -3484,12 +3498,13 @@ UINT AwtComponent::WindowsKeyToJavaChar(UINT wkey, UINT modifiers, TransOps ops) // instead of creating our own conversion tables, I'll let Win32 // convert the character for me. - WORD mbChar; + WORD wChar[2]; UINT scancode = ::MapVirtualKey(wkey, 0); - int converted = ::ToAsciiEx(wkey, scancode, keyboardState, - &mbChar, 0, GetKeyboardLayout()); + int converted = ::ToUnicodeEx(wkey, scancode, keyboardState, + wChar, 2, 0, GetKeyboardLayout()); UINT translation; + BOOL deadKeyFlag = (converted == 2); // Dead Key if (converted < 0) { @@ -3508,16 +3523,16 @@ UINT AwtComponent::WindowsKeyToJavaChar(UINT wkey, UINT modifiers, TransOps ops) } else // the caller expects a Unicode character. if (converted > 0) { - WCHAR unicodeChar[2]; - VERIFY(::MultiByteToWideChar(GetCodePage(), MB_PRECOMPOSED, - (LPCSTR)&mbChar, 1, unicodeChar, 1)); - - translation = unicodeChar[0]; + translation = wChar[0]; } if (ops == SAVE) { transTable.put(reinterpret_cast(static_cast(wkey)), reinterpret_cast(static_cast(translation))); + deadKeyFlagTable.put(reinterpret_cast(static_cast(wkey)), + reinterpret_cast(static_cast(deadKeyFlag))); } + + isDeadKey = deadKeyFlag; return translation; } @@ -3537,8 +3552,9 @@ MsgRouting AwtComponent::WmKeyDown(UINT wkey, UINT repCnt, UINT modifiers = GetJavaModifiers(); jint keyLocation = GetKeyLocation(wkey, flags); - UINT jkey = WindowsKeyToJavaKey(wkey, modifiers); - UINT character = WindowsKeyToJavaChar(wkey, modifiers, SAVE); + BOOL isDeadKey = FALSE; + UINT character = WindowsKeyToJavaChar(wkey, modifiers, SAVE, isDeadKey); + UINT jkey = WindowsKeyToJavaKey(wkey, modifiers, character, isDeadKey); UpdateDynPrimaryKeymap(wkey, jkey, keyLocation, modifiers); @@ -3579,8 +3595,9 @@ MsgRouting AwtComponent::WmKeyUp(UINT wkey, UINT repCnt, UINT modifiers = GetJavaModifiers(); jint keyLocation = GetKeyLocation(wkey, flags); - UINT jkey = WindowsKeyToJavaKey(wkey, modifiers); - UINT character = WindowsKeyToJavaChar(wkey, modifiers, LOAD); + BOOL isDeadKey = FALSE; + UINT character = WindowsKeyToJavaChar(wkey, modifiers, LOAD, isDeadKey); + UINT jkey = WindowsKeyToJavaKey(wkey, modifiers, character, isDeadKey); UpdateDynPrimaryKeymap(wkey, jkey, keyLocation, modifiers); SendKeyEventToFocusOwner(java_awt_event_KeyEvent_KEY_RELEASED, @@ -5628,7 +5645,8 @@ void AwtComponent::_NativeHandleEvent(void *param) } } - modifiedChar = p->WindowsKeyToJavaChar(winKey, modifiers, AwtComponent::NONE); + BOOL isDeadKey = FALSE; + modifiedChar = p->WindowsKeyToJavaChar(winKey, modifiers, AwtComponent::NONE, isDeadKey); bCharChanged = (keyChar != modifiedChar); } break; @@ -7166,4 +7184,4 @@ void ReleaseDCList(HWND hwnd, DCList &list) { removedDCs = removedDCs->next; delete tmpDCList; } -} +} \ No newline at end of file diff --git a/jdk/src/windows/native/sun/windows/awt_Component.h b/jdk/src/windows/native/sun/windows/awt_Component.h index 75c1b3c9384..8ce8fa39112 100644 --- a/jdk/src/windows/native/sun/windows/awt_Component.h +++ b/jdk/src/windows/native/sun/windows/awt_Component.h @@ -441,7 +441,7 @@ public: static jint GetJavaModifiers(); static jint GetButton(int mouseButton); static UINT GetButtonMK(int mouseButton); - static UINT WindowsKeyToJavaKey(UINT windowsKey, UINT modifiers); + static UINT WindowsKeyToJavaKey(UINT windowsKey, UINT modifiers, UINT character, BOOL isDeadKey); static void JavaKeyToWindowsKey(UINT javaKey, UINT *windowsKey, UINT *modifiers, UINT originalWindowsKey); static void UpdateDynPrimaryKeymap(UINT wkey, UINT jkeyLegacy, jint keyLocation, UINT modifiers); @@ -453,7 +453,7 @@ public: enum TransOps {NONE, LOAD, SAVE}; - UINT WindowsKeyToJavaChar(UINT wkey, UINT modifiers, TransOps ops); + UINT WindowsKeyToJavaChar(UINT wkey, UINT modifiers, TransOps ops, BOOL &isDeadKey); /* routines used for input method support */ void SetInputMethod(jobject im, BOOL useNativeCompWindow); From ebff918cc74c172569caa3c90ce38200460e4d03 Mon Sep 17 00:00:00 2001 From: Oleg Pekhovskiy Date: Wed, 3 Oct 2012 21:01:47 +0400 Subject: [PATCH 9/9] 7171412: awt Choice doesn't fire ItemStateChange when selecting item after select() call Reviewed-by: art, denis --- jdk/src/macosx/native/sun/awt/InitIDs.m | 5 +- jdk/src/share/classes/java/awt/Choice.java | 15 +- jdk/src/solaris/native/sun/awt/initIDs.c | 6 + .../windows/native/sun/windows/awt_Choice.cpp | 27 +++- .../windows/native/sun/windows/awt_Choice.h | 1 - .../ItemStateChangeTest.java | 128 ++++++++++++++++++ 6 files changed, 173 insertions(+), 9 deletions(-) create mode 100644 jdk/test/java/awt/Choice/ItemStateChangeTest/ItemStateChangeTest.java diff --git a/jdk/src/macosx/native/sun/awt/InitIDs.m b/jdk/src/macosx/native/sun/awt/InitIDs.m index d8b0fdccf7d..2b1c007fd4c 100644 --- a/jdk/src/macosx/native/sun/awt/InitIDs.m +++ b/jdk/src/macosx/native/sun/awt/InitIDs.m @@ -47,7 +47,10 @@ JNIEXPORT void JNICALL Java_java_awt_CheckboxMenuItem_initIDs { } - +JNIEXPORT void JNICALL Java_java_awt_Choice_initIDs +(JNIEnv *env, jclass cls) +{ +} JNIEXPORT void JNICALL Java_java_awt_Color_initIDs (JNIEnv *env, jclass cls) diff --git a/jdk/src/share/classes/java/awt/Choice.java b/jdk/src/share/classes/java/awt/Choice.java index d7d55a24dc2..895be89a615 100644 --- a/jdk/src/share/classes/java/awt/Choice.java +++ b/jdk/src/share/classes/java/awt/Choice.java @@ -104,7 +104,16 @@ public class Choice extends Component implements ItemSelectable, Accessible { /* * JDK 1.1 serialVersionUID */ - private static final long serialVersionUID = -4075310674757313071L; + private static final long serialVersionUID = -4075310674757313071L; + + static { + /* ensure that the necessary native libraries are loaded */ + Toolkit.loadLibraries(); + /* initialize JNI field and method ids */ + if (!GraphicsEnvironment.isHeadless()) { + initIDs(); + } + } /** * Creates a new choice menu. The menu initially has no items in it. @@ -711,6 +720,10 @@ public class Choice extends Component implements ItemSelectable, Accessible { } } + /** + * Initialize JNI field and method IDs + */ + private static native void initIDs(); ///////////////// // Accessibility support diff --git a/jdk/src/solaris/native/sun/awt/initIDs.c b/jdk/src/solaris/native/sun/awt/initIDs.c index 3bd1e68903c..3c9a388f95c 100644 --- a/jdk/src/solaris/native/sun/awt/initIDs.c +++ b/jdk/src/solaris/native/sun/awt/initIDs.c @@ -88,6 +88,12 @@ Java_java_awt_CheckboxMenuItem_initIDs { } +JNIEXPORT void JNICALL +Java_java_awt_Choice_initIDs + (JNIEnv *env, jclass clazz) +{ +} + JNIEXPORT void JNICALL Java_java_awt_Dimension_initIDs (JNIEnv *env, jclass clazz) diff --git a/jdk/src/windows/native/sun/windows/awt_Choice.cpp b/jdk/src/windows/native/sun/windows/awt_Choice.cpp index dfd9a6de6b3..5b9647fd7a7 100644 --- a/jdk/src/windows/native/sun/windows/awt_Choice.cpp +++ b/jdk/src/windows/native/sun/windows/awt_Choice.cpp @@ -79,6 +79,10 @@ BOOL AwtChoice::sm_isMouseMoveInList = FALSE; static const UINT MINIMUM_NUMBER_OF_VISIBLE_ITEMS = 8; +namespace { + jfieldID selectedIndexID; +} + /************************************************************************* * AwtChoice class methods */ @@ -86,7 +90,6 @@ static const UINT MINIMUM_NUMBER_OF_VISIBLE_ITEMS = 8; AwtChoice::AwtChoice() { m_hList = NULL; m_listDefWindowProc = NULL; - m_selectedItem = -1; } LPCTSTR AwtChoice::GetClassName() { @@ -102,7 +105,6 @@ void AwtChoice::Dispose() { AwtChoice* AwtChoice::Create(jobject peer, jobject parent) { - JNIEnv *env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2); jobject target = NULL; @@ -438,10 +440,14 @@ LRESULT CALLBACK AwtChoice::ListWindowProc(HWND hwnd, UINT message, MsgRouting AwtChoice::WmNotify(UINT notifyCode) { if (notifyCode == CBN_SELCHANGE) { - int selectedItem = (int)SendMessage(CB_GETCURSEL); - if (selectedItem != CB_ERR && m_selectedItem != selectedItem){ - m_selectedItem = selectedItem; - DoCallback("handleAction", "(I)V", selectedItem); + int selectedIndex = (int)SendMessage(CB_GETCURSEL); + + JNIEnv *env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2); + jobject target = GetTarget(env); + int previousIndex = env->GetIntField(target, selectedIndexID); + + if (selectedIndex != CB_ERR && selectedIndex != previousIndex){ + DoCallback("handleAction", "(I)V", selectedIndex); } } else if (notifyCode == CBN_DROPDOWN) { @@ -695,6 +701,15 @@ done: extern "C" { +JNIEXPORT void JNICALL +Java_java_awt_Choice_initIDs(JNIEnv *env, jclass cls) +{ + TRY; + selectedIndexID = env->GetFieldID(cls, "selectedIndex", "I"); + DASSERT(selectedIndexID); + CATCH_BAD_ALLOC; +} + /* * Class: sun_awt_windows_WChoicePeer * Method: select diff --git a/jdk/src/windows/native/sun/windows/awt_Choice.h b/jdk/src/windows/native/sun/windows/awt_Choice.h index 194ae08bd1b..36be8d91aa5 100644 --- a/jdk/src/windows/native/sun/windows/awt_Choice.h +++ b/jdk/src/windows/native/sun/windows/awt_Choice.h @@ -94,7 +94,6 @@ private: static BOOL sm_isMouseMoveInList; HWND m_hList; WNDPROC m_listDefWindowProc; - int m_selectedItem; static LRESULT CALLBACK ListWindowProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam); }; diff --git a/jdk/test/java/awt/Choice/ItemStateChangeTest/ItemStateChangeTest.java b/jdk/test/java/awt/Choice/ItemStateChangeTest/ItemStateChangeTest.java new file mode 100644 index 00000000000..45ccef0262b --- /dev/null +++ b/jdk/test/java/awt/Choice/ItemStateChangeTest/ItemStateChangeTest.java @@ -0,0 +1,128 @@ +/* + * Copyright (c) 2012, 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. + * + * 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. + */ + +/* + @test + @bug 7171412 + @summary awt Choice doesn't fire ItemStateChange when selecting item after select() call + @author Oleg Pekhovskiy: area=awt-choice + @library ../../regtesthelpers + @build Util + @run main ItemStateChangeTest +*/ + +import test.java.awt.regtesthelpers.Util; + +import java.awt.*; +import java.awt.event.*; +import sun.awt.OSInfo; + +public class ItemStateChangeTest extends Frame { + + int events = 0; + + public static void main(String args[]) { + new ItemStateChangeTest(); + } + + public ItemStateChangeTest() { + + if (OSInfo.getOSType() != OSInfo.OSType.WINDOWS) { + return; + } + + try { + + final Robot robot = new Robot(); + robot.setAutoDelay(20); + Util.waitForIdle(robot); + + addWindowListener(new WindowAdapter() { + @Override + public void windowClosing(WindowEvent e) { + System.exit(0); + } + }); + + final Choice choice = new Choice(); + choice.add("A"); + choice.add("B"); + choice.addItemListener(new ItemListener() { + @Override + public void itemStateChanged(ItemEvent e) { + ++events; + } + }); + + add(choice); + setSize(200, 150); + setVisible(true); + toFront(); + + // choose B + int y = chooseB(choice, robot, 16); + + // reset to A + choice.select(0); + robot.delay(20); + Util.waitForIdle(robot); + + // choose B again + chooseB(choice, robot, y); + + if (events == 2) { + System.out.println("Test passed!"); + } + else { + throw new RuntimeException("Test failed!"); + } + + } + catch (AWTException e) { + throw new RuntimeException("Test failed!"); + } + } + + final int chooseB(Choice choice, Robot robot, int y) { + while (true) { + // show drop-down list + Util.clickOnComp(choice, robot); + Util.waitForIdle(robot); + Point pt = choice.getLocationOnScreen(); + Dimension size = choice.getSize(); + // try to click B item + robot.mouseMove(pt.x + size.width / 2, pt.y + size.height + y); + Util.waitForIdle(robot); + robot.mousePress(InputEvent.BUTTON1_DOWN_MASK); + Util.waitForIdle(robot); + robot.mouseRelease(InputEvent.BUTTON1_DOWN_MASK); + Util.waitForIdle(robot); + if (choice.getSelectedIndex() == 1) { + break; + } + // if it's not B, position cursor lower by 2 pixels and try again + y += 2; + } + return y; + } +}