diff --git a/src/share/classes/java/awt/Dialog.java b/src/share/classes/java/awt/Dialog.java index 71b08d7226d838fc6880ac8fc2f3fd90eda4d4b6..3e2effd1dc059733f7af891f5a03547bc517b1d2 100644 --- a/src/share/classes/java/awt/Dialog.java +++ b/src/share/classes/java/awt/Dialog.java @@ -277,10 +277,8 @@ public class Dialog extends Window { */ String title; - private transient volatile boolean keepBlockingEDT = false; - private transient volatile boolean keepBlockingCT = false; - private transient ModalEventFilter modalFilter; + private transient volatile SecondaryLoop secondaryLoop; /* * Indicates that this dialog is being hidden. This flag is set to true at @@ -1005,12 +1003,6 @@ public class Dialog extends Window { super.setVisible(b); } - /** - * Stores the app context on which event dispatch thread the dialog - * is being shown. Initialized in show(), used in hideAndDisposeHandler() - */ - transient private AppContext showAppContext; - /** * Makes the {@code Dialog} visible. If the dialog and/or its owner * are not yet displayable, both are made displayable. The @@ -1037,39 +1029,18 @@ public class Dialog extends Window { if (!isModal()) { conditionalShow(null, null); } else { - // Set this variable before calling conditionalShow(). That - // way, if the Dialog is hidden right after being shown, we - // won't mistakenly block this thread. - keepBlockingEDT = true; - keepBlockingCT = true; - - // Store the app context on which this dialog is being shown. - // Event dispatch thread of this app context will be sleeping until - // we wake it by any event from hideAndDisposeHandler(). - showAppContext = AppContext.getAppContext(); + AppContext showAppContext = AppContext.getAppContext(); AtomicLong time = new AtomicLong(); Component predictedFocusOwner = null; try { predictedFocusOwner = getMostRecentFocusOwner(); if (conditionalShow(predictedFocusOwner, time)) { - // We have two mechanisms for blocking: 1. If we're on the - // EventDispatchThread, start a new event pump. 2. If we're - // on any other thread, call wait() on the treelock. - modalFilter = ModalEventFilter.createFilterForDialog(this); - - final Runnable pumpEventsForFilter = new Runnable() { - public void run() { - EventDispatchThread dispatchThread = - (EventDispatchThread)Thread.currentThread(); - dispatchThread.pumpEventsForFilter(new Conditional() { - public boolean evaluate() { - synchronized (getTreeLock()) { - return keepBlockingEDT && windowClosingException == null; - } - } - }, modalFilter); + Conditional cond = new Conditional() { + @Override + public boolean evaluate() { + return windowClosingException == null; } }; @@ -1096,44 +1067,10 @@ public class Dialog extends Window { modalityPushed(); try { - if (EventQueue.isDispatchThread()) { - /* - * dispose SequencedEvent we are dispatching on current - * AppContext, to prevent us from hang. - * - */ - // BugId 4531693 (son@sparc.spb.su) - SequencedEvent currentSequencedEvent = KeyboardFocusManager. - getCurrentKeyboardFocusManager().getCurrentSequencedEvent(); - if (currentSequencedEvent != null) { - currentSequencedEvent.dispose(); - } - - /* - * Event processing is done inside doPrivileged block so that - * it wouldn't matter even if user code is on the stack - * Fix for BugId 6300270 - */ - - AccessController.doPrivileged(new PrivilegedAction() { - public Object run() { - pumpEventsForFilter.run(); - return null; - } - }); - } else { - synchronized (getTreeLock()) { - Toolkit.getEventQueue().postEvent(new PeerEvent(this, - pumpEventsForFilter, - PeerEvent.PRIORITY_EVENT)); - while (keepBlockingCT && windowClosingException == null) { - try { - getTreeLock().wait(); - } catch (InterruptedException e) { - break; - } - } - } + EventQueue eventQueue = Toolkit.getDefaultToolkit().getSystemEventQueue(); + secondaryLoop = eventQueue.createSecondaryLoop(cond, modalFilter, 5000); + if (!secondaryLoop.enter()) { + secondaryLoop = null; } } finally { modalityPopped(); @@ -1194,18 +1131,11 @@ public class Dialog extends Window { windowClosingException = null; } } - final class WakingRunnable implements Runnable { - public void run() { - synchronized (getTreeLock()) { - keepBlockingCT = false; - getTreeLock().notifyAll(); - } - } - } + private void hideAndDisposePreHandler() { isInHide = true; synchronized (getTreeLock()) { - if (keepBlockingEDT) { + if (secondaryLoop != null) { modalHide(); // dialog can be shown and then disposed before its // modal filter is created @@ -1217,20 +1147,9 @@ public class Dialog extends Window { } } private void hideAndDisposeHandler() { - synchronized (getTreeLock()) { - if (keepBlockingEDT) { - keepBlockingEDT = false; - PeerEvent wakingEvent = new PeerEvent(getToolkit(), new WakingRunnable(), PeerEvent.PRIORITY_EVENT); - AppContext curAppContext = AppContext.getAppContext(); - if (showAppContext != curAppContext) { - // Wake up event dispatch thread on which the dialog was - // initially shown - SunToolkit.postEvent(showAppContext, wakingEvent); - showAppContext = null; - } else { - Toolkit.getEventQueue().postEvent(wakingEvent); - } - } + if (secondaryLoop != null) { + secondaryLoop.exit(); + secondaryLoop = null; } isInHide = false; } diff --git a/src/share/classes/java/awt/EventDispatchThread.java b/src/share/classes/java/awt/EventDispatchThread.java index eed2c16e514281a8c24c6c307bae8bae932db043..b47c3686e81b7110cbe59457531d1960cd7e02cf 100644 --- a/src/share/classes/java/awt/EventDispatchThread.java +++ b/src/share/classes/java/awt/EventDispatchThread.java @@ -113,8 +113,7 @@ class EventDispatchThread extends Thread { pumpEventsForHierarchy(id, cond, null); } - void pumpEventsForHierarchy(int id, Conditional cond, Component modalComponent) - { + void pumpEventsForHierarchy(int id, Conditional cond, Component modalComponent) { pumpEventsForFilter(id, cond, new HierarchyEventFilter(modalComponent)); } @@ -124,6 +123,7 @@ class EventDispatchThread extends Thread { void pumpEventsForFilter(int id, Conditional cond, EventFilter filter) { addEventFilter(filter); + doDispatch = true; while (doDispatch && cond.evaluate()) { if (isInterrupted() || !pumpOneEventForFilters(id)) { doDispatch = false; @@ -133,6 +133,7 @@ class EventDispatchThread extends Thread { } void addEventFilter(EventFilter filter) { + eventLog.finest("adding the event filter: " + filter); synchronized (eventFilters) { if (!eventFilters.contains(filter)) { if (filter instanceof ModalEventFilter) { @@ -156,6 +157,7 @@ class EventDispatchThread extends Thread { } void removeEventFilter(EventFilter filter) { + eventLog.finest("removing the event filter: " + filter); synchronized (eventFilters) { eventFilters.remove(filter); } diff --git a/src/share/classes/java/awt/EventQueue.java b/src/share/classes/java/awt/EventQueue.java index 86c68e8b5c7f353525774a087b4c40fcaee2f86f..ffda53b69e7567f97125195984d830a76beddb7a 100644 --- a/src/share/classes/java/awt/EventQueue.java +++ b/src/share/classes/java/awt/EventQueue.java @@ -883,6 +883,41 @@ public class EventQueue { } } + /** + * Creates a new {@code secondary loop} associated with this + * event queue. Use the {@link SecondaryLoop#enter} and + * {@link SecondaryLoop#exit} methods to start and stop the + * event loop and dispatch the events from this queue. + * + * @return secondaryLoop A new secondary loop object, which can + * be used to launch a new nested event + * loop and dispatch events from this queue + * + * @see SecondaryLoop#enter + * @see SecondaryLoop#exit + * + * @since 1.7 + */ + public SecondaryLoop createSecondaryLoop() { + return createSecondaryLoop(null, null, 0); + } + + SecondaryLoop createSecondaryLoop(Conditional cond, EventFilter filter, long interval) { + pushPopLock.lock(); + try { + if (nextQueue != null) { + // Forward the request to the top of EventQueue stack + return nextQueue.createSecondaryLoop(cond, filter, interval); + } + if (dispatchThread == null) { + initDispatchThread(); + } + return new WaitDispatchSupport(dispatchThread, cond, filter, interval); + } finally { + pushPopLock.unlock(); + } + } + /** * Returns true if the calling thread is * {@link Toolkit#getSystemEventQueue the current AWT EventQueue}'s diff --git a/src/share/classes/java/awt/SecondaryLoop.java b/src/share/classes/java/awt/SecondaryLoop.java new file mode 100644 index 0000000000000000000000000000000000000000..844efc85261547d82facb22bb42075a1e4936658 --- /dev/null +++ b/src/share/classes/java/awt/SecondaryLoop.java @@ -0,0 +1,147 @@ +/* + * Copyright (c) 2010, 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 java.awt; + +/** + * A helper interface to run the nested event loop. + *

+ * Objects that implement this interface are created with the + * {@link EventQueue#createSecondaryLoop} method. The interface + * provides two methods, {@link enter} and {@link exit}, + * which can be used to start and stop the event loop. + *

+ * When the {@link enter} method is called, the current + * thread is blocked until the loop is terminated by the + * {@link exit} method. Also, a new event loop is started + * on the event dispatch thread, which may or may not be + * the current thread. The loop can be terminated on any + * thread by calling its {@link exit} method. After the + * loop is terminated, the {@code SecondaryLoop} object can + * be reused to run a new nested event loop. + *

+ * A typical use case of applying this interface is AWT + * and Swing modal dialogs. When a modal dialog is shown on + * the event dispatch thread, it enters a new secondary loop. + * Later, when the dialog is hidden or disposed, it exits + * the loop, and the thread continues its execution. + *

+ * The following example illustrates a simple use case of + * secondary loops: + * + *

+ *   SecondaryLoop loop;
+ *
+ *   JButton jButton = new JButton("Button");
+ *   jButton.addActionListener(new ActionListener() {
+ *       {@code @Override}
+ *       public void actionPerformed(ActionEvent e) {
+ *           Toolkit tk = Toolkit.getDefaultToolkit();
+ *           EventQueue eq = tk.getSystemEventQueue();
+ *           loop = eq.createSecondaryLoop();
+ *
+ *           // Spawn a new thread to do the work
+ *           Thread worker = new WorkerThread();
+ *           worker.start();
+ *
+ *           // Enter the loop to block the current event
+ *           // handler, but leave UI responsive
+ *           if (!loop.enter()) {
+ *               // Report an error
+ *           }
+ *       }
+ *   });
+ *
+ *   class WorkerThread extends Thread {
+ *       {@code @Override}
+ *       public void run() {
+ *           // Perform calculations
+ *           doSomethingUseful();
+ *
+ *           // Exit the loop
+ *           loop.exit();
+ *       }
+ *   }
+ * 
+ * + * @see Dialog#show + * @see EventQueue#createSecondaryLoop + * @see Toolkit#getSystemEventQueue + * + * @author Anton Tarasov, Artem Ananiev + * + * @since 1.7 + */ +public interface SecondaryLoop { + + /** + * Blocks the execution of the current thread and enters a new + * secondary event loop on the event dispatch thread. + *

+ * This method can be called by any thread including the event + * dispatch thread. This thread will be blocked until the {@link + * exit} method is called or the loop is terminated. A new + * secondary loop will be created on the event dispatch thread + * for dispatching events in either case. + *

+ * This method can only start one new event loop at a time per + * object. If a secondary event loop has already been started + * by this object and is currently still running, this method + * returns {@code false} to indicate that it was not successful + * in starting a new event loop. Otherwise, this method blocks + * the calling thread and later returns {@code true} when the + * new event loop is terminated. At such time, this object can + * again be used to start another new event loop. + * + * @return {@code true} after termination of the secondary loop, + * if the secondary loop was started by this call, + * {@code false} otherwise + */ + public boolean enter(); + + /** + * Unblocks the execution of the thread blocked by the {@link + * enter} method and exits the secondary loop. + *

+ * This method resumes the thread that called the {@link enter} + * method and exits the secondary loop that was created when + * the {@link enter} method was invoked. + *

+ * Note that if any other secondary loop is started while this + * loop is running, the blocked thread will not resume execution + * until the nested loop is terminated. + *

+ * If this secondary loop has not been started with the {@link + * enter} method, or this secondary loop has already finished + * with the {@link exit} method, this method returns {@code + * false}, otherwise {@code true} is returned. + * + * @return {@code true} if this loop was previously started and + * has not yet been finished with the {@link exit} method, + * {@code false} otherwise + */ + public boolean exit(); + +} diff --git a/src/share/classes/java/awt/WaitDispatchSupport.java b/src/share/classes/java/awt/WaitDispatchSupport.java new file mode 100644 index 0000000000000000000000000000000000000000..bf77fa73a62dcc34f8733d3c6d68b460f279d80c --- /dev/null +++ b/src/share/classes/java/awt/WaitDispatchSupport.java @@ -0,0 +1,302 @@ +/* + * Copyright (c) 2010, 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 java.awt; + +import java.util.Timer; +import java.util.TimerTask; +import java.util.concurrent.atomic.AtomicBoolean; + +import java.security.PrivilegedAction; +import java.security.AccessController; + +import sun.awt.PeerEvent; + +import sun.util.logging.PlatformLogger; + +/** + * This utility class is used to suspend execution on a thread + * while still allowing {@code EventDispatchThread} to dispatch events. + * The API methods of the class are thread-safe. + * + * @author Anton Tarasov, Artem Ananiev + * + * @since 1.7 + */ +class WaitDispatchSupport implements SecondaryLoop { + + private final static PlatformLogger log = + PlatformLogger.getLogger("java.awt.event.WaitDispatchSupport"); + + private EventDispatchThread dispatchThread; + private EventFilter filter; + + private volatile Conditional extCondition; + private volatile Conditional condition; + + private long interval; + // Use a shared daemon timer to serve all the WaitDispatchSupports + private static Timer timer; + // When this WDS expires, we cancel the timer task leaving the + // shared timer up and running + private TimerTask timerTask; + + private AtomicBoolean keepBlockingEDT = new AtomicBoolean(false); + private AtomicBoolean keepBlockingCT = new AtomicBoolean(false); + + private static synchronized void initializeTimer() { + if (timer == null) { + timer = new Timer("AWT-WaitDispatchSupport-Timer", true); + } + } + + /** + * Creates a {@code WaitDispatchSupport} instance to + * serve the given event dispatch thread. + * + * @param dispatchThread An event dispatch thread that + * should not stop dispatching events while waiting + * + * @since 1.7 + */ + public WaitDispatchSupport(EventDispatchThread dispatchThread) { + this(dispatchThread, null); + } + + /** + * Creates a {@code WaitDispatchSupport} instance to + * serve the given event dispatch thread. + * + * @param dispatchThread An event dispatch thread that + * should not stop dispatching events while waiting + * @param extCondition A conditional object used to determine + * if the loop should be terminated + * + * @since 1.7 + */ + public WaitDispatchSupport(EventDispatchThread dispatchThread, + Conditional extCond) + { + if (dispatchThread == null) { + throw new IllegalArgumentException("The dispatchThread can not be null"); + } + + this.dispatchThread = dispatchThread; + this.extCondition = extCond; + this.condition = new Conditional() { + @Override + public boolean evaluate() { + if (log.isLoggable(PlatformLogger.FINEST)) { + log.finest("evaluate(): blockingEDT=" + keepBlockingEDT.get() + + ", blockingCT=" + keepBlockingCT.get()); + } + boolean extEvaluate = + (extCondition != null) ? extCondition.evaluate() : true; + if (!keepBlockingEDT.get() || !extEvaluate) { + if (timerTask != null) { + timerTask.cancel(); + timerTask = null; + } + return false; + } + return true; + } + }; + } + + /** + * Creates a {@code WaitDispatchSupport} instance to + * serve the given event dispatch thread. + *

+ * The {@link EventFilter} is set on the {@code dispatchThread} + * while waiting. The filter is removed on completion of the + * waiting process. + *

+ * + * + * @param dispatchThread An event dispatch thread that + * should not stop dispatching events while waiting + * @param filter {@code EventFilter} to be set + * @param interval A time interval to wait for. Note that + * when the waiting process takes place on EDT + * there is no guarantee to stop it in the given time + * + * @since 1.7 + */ + public WaitDispatchSupport(EventDispatchThread dispatchThread, + Conditional extCondition, + EventFilter filter, long interval) + { + this(dispatchThread, extCondition); + this.filter = filter; + if (interval < 0) { + throw new IllegalArgumentException("The interval value must be >= 0"); + } + this.interval = interval; + if (interval != 0) { + initializeTimer(); + } + } + + /** + * @inheritDoc + */ + @Override + public boolean enter() { + log.fine("enter(): blockingEDT=" + keepBlockingEDT.get() + + ", blockingCT=" + keepBlockingCT.get()); + + if (!keepBlockingEDT.compareAndSet(false, true)) { + log.fine("The secondary loop is already running, aborting"); + return false; + } + + final Runnable run = new Runnable() { + public void run() { + log.fine("Starting a new event pump"); + if (filter == null) { + dispatchThread.pumpEvents(condition); + } else { + dispatchThread.pumpEventsForFilter(condition, filter); + } + } + }; + + // We have two mechanisms for blocking: if we're on the + // dispatch thread, start a new event pump; if we're + // on any other thread, call wait() on the treelock + + Thread currentThread = Thread.currentThread(); + if (currentThread == dispatchThread) { + log.finest("On dispatch thread: " + dispatchThread); + if (interval != 0) { + log.finest("scheduling the timer for " + interval + " ms"); + timer.schedule(timerTask = new TimerTask() { + @Override + public void run() { + if (keepBlockingEDT.compareAndSet(true, false)) { + wakeupEDT(); + } + } + }, interval); + } + // Dispose SequencedEvent we are dispatching on the the current + // AppContext, to prevent us from hang - see 4531693 for details + SequencedEvent currentSE = KeyboardFocusManager. + getCurrentKeyboardFocusManager().getCurrentSequencedEvent(); + if (currentSE != null) { + log.fine("Dispose current SequencedEvent: " + currentSE); + currentSE.dispose(); + } + // In case the exit() method is called before starting + // new event pump it will post the waking event to EDT. + // The event will be handled after the the new event pump + // starts. Thus, the enter() method will not hang. + // + // Event pump should be privileged. See 6300270. + AccessController.doPrivileged(new PrivilegedAction() { + public Object run() { + run.run(); + return null; + } + }); + } else { + log.finest("On non-dispatch thread: " + currentThread); + synchronized (getTreeLock()) { + if (filter != null) { + dispatchThread.addEventFilter(filter); + } + try { + EventQueue eq = dispatchThread.getEventQueue(); + eq.postEvent(new PeerEvent(this, run, PeerEvent.PRIORITY_EVENT)); + keepBlockingCT.set(true); + if (interval > 0) { + long currTime = System.currentTimeMillis(); + while (keepBlockingCT.get() && + ((extCondition != null) ? extCondition.evaluate() : true) && + (currTime + interval > System.currentTimeMillis())) + { + getTreeLock().wait(interval); + } + } else { + while (keepBlockingCT.get() && + ((extCondition != null) ? extCondition.evaluate() : true)) + { + getTreeLock().wait(); + } + } + log.fine("waitDone " + keepBlockingEDT.get() + " " + keepBlockingCT.get()); + } catch (InterruptedException e) { + log.fine("Exception caught while waiting: " + e); + } finally { + if (filter != null) { + dispatchThread.removeEventFilter(filter); + } + } + // If the waiting process has been stopped because of the + // time interval passed or an exception occurred, the state + // should be changed + keepBlockingEDT.set(false); + keepBlockingCT.set(false); + } + } + + return true; + } + + /** + * @inheritDoc + */ + public boolean exit() { + log.fine("exit(): blockingEDT=" + keepBlockingEDT.get() + + ", blockingCT=" + keepBlockingCT.get()); + if (keepBlockingEDT.compareAndSet(true, false)) { + wakeupEDT(); + return true; + } + return false; + } + + private final static Object getTreeLock() { + return Component.LOCK; + } + + private final Runnable wakingRunnable = new Runnable() { + public void run() { + log.fine("Wake up EDT"); + synchronized (getTreeLock()) { + keepBlockingCT.set(false); + getTreeLock().notifyAll(); + } + log.fine("Wake up EDT done"); + } + }; + + private void wakeupEDT() { + log.finest("wakeupEDT(): EDT == " + dispatchThread); + EventQueue eq = dispatchThread.getEventQueue(); + eq.postEvent(new PeerEvent(this, wakingRunnable, PeerEvent.PRIORITY_EVENT)); + } +} diff --git a/src/windows/native/sun/windows/awt_MenuItem.cpp b/src/windows/native/sun/windows/awt_MenuItem.cpp index 67b2fa262a8caf4d866e3df64e20016812be1a88..6ed2a73d6afea7a5c9a2ce33ea595dcb7f361751 100644 --- a/src/windows/native/sun/windows/awt_MenuItem.cpp +++ b/src/windows/native/sun/windows/awt_MenuItem.cpp @@ -794,6 +794,11 @@ BOOL AwtMenuItem::IsSeparator() { jobject jitem = GetTarget(env); jstring label = (jstring)(env)->GetObjectField(jitem, AwtMenuItem::labelID); + if (label == NULL) { + env->DeleteLocalRef(label); + env->DeleteLocalRef(jitem); + return FALSE; //separator must has '-' as label. + } LPCWSTR labelW = JNU_GetStringPlatformChars(env, label, NULL); BOOL isSeparator = (labelW && (wcscmp(labelW, L"-") == 0)); JNU_ReleaseStringPlatformChars(env, label, labelW); diff --git a/src/windows/native/sun/windows/awt_TextComponent.h b/src/windows/native/sun/windows/awt_TextComponent.h index 3a993742d118206725d8eaae2551103b1b50d436..7581c5c7aae673a8e93850cd607d1fef8257c879 100644 --- a/src/windows/native/sun/windows/awt_TextComponent.h +++ b/src/windows/native/sun/windows/awt_TextComponent.h @@ -113,8 +113,6 @@ public: // Used to prevent untrusted code from synthesizing a WM_PASTE message // by posting a -V KeyEvent BOOL m_synthetic; - virtual void EditSetSel(CHARRANGE &cr) = 0; - virtual void EditGetSel(CHARRANGE &cr) = 0; virtual LONG EditGetCharFromPos(POINT& pt) = 0; private: diff --git a/src/windows/native/sun/windows/awt_TextField.cpp b/src/windows/native/sun/windows/awt_TextField.cpp index 94e8baca1b2c528e7741806938ad6a0a538b179b..8f72dc06b56ab61d2fc19e31479638bd9cde0c78 100644 --- a/src/windows/native/sun/windows/awt_TextField.cpp +++ b/src/windows/native/sun/windows/awt_TextField.cpp @@ -41,7 +41,9 @@ struct SetEchoCharStruct { * AwtTextField methods */ -AwtTextField::AwtTextField() { +AwtTextField::AwtTextField() + : m_initialRescrollFlag( true ) +{ } /* Create a new AwtTextField object and window. */ @@ -116,10 +118,6 @@ void AwtTextField::EditSetSel(CHARRANGE &cr) { SendMessage(EM_SETSEL, cr.cpMin, cr.cpMax); } -void AwtTextField::EditGetSel(CHARRANGE &cr) { - SendMessage(EM_SETSEL, reinterpret_cast(&cr.cpMin), reinterpret_cast(&cr.cpMax)); -} - LONG AwtTextField::EditGetCharFromPos(POINT& pt) { return static_cast(SendMessage(EM_CHARFROMPOS, 0, MAKELPARAM(pt.x, pt.y))); } @@ -153,11 +151,9 @@ AwtTextField::HandleEvent(MSG *msg, BOOL synthetic) * The workaround also allows us to implement synthetic focus mechanism. */ if (IsFocusingMouseMessage(msg)) { - CHARRANGE cr; LONG lCurPos = EditGetCharFromPos(msg->pt); - EditGetSel(cr); /* * NOTE: Plain EDIT control always clears selection on mouse * button press. We are clearing the current selection only if @@ -174,6 +170,7 @@ AwtTextField::HandleEvent(MSG *msg, BOOL synthetic) SetStartSelectionPos(lCurPos); SetEndSelectionPos(lCurPos); } + CHARRANGE cr; cr.cpMin = GetStartSelectionPos(); cr.cpMax = GetEndSelectionPos(); EditSetSel(cr); @@ -310,6 +307,47 @@ ret: delete secs; } +void AwtTextField::Reshape(int x, int y, int w, int h) +{ + AwtTextComponent::Reshape( x, y, w, h ); + + // Another option would be to call this + // after WM_SIZE notification is handled + initialRescroll(); +} + + +// Windows' Edit control features: +// (i) if text selection is set while control's width or height is 0, +// text is scrolled oddly. +// (ii) if control's size is changed, text seems never be automatically +// rescrolled. +// +// This method is designed for the following scenario: AWT spawns Edit +// control with 0x0 dimensions, then sets text selection, then resizes the +// control (couple of times). This might cause text appear undesirably scrolled. +// So we reset/set selection again to rescroll text. (see also CR 6480547) +void AwtTextField::initialRescroll() +{ + if( ! m_initialRescrollFlag ) { + return; + } + + ::RECT r; + BOOL ok = ::GetClientRect( GetHWnd(), &r ); + if( ! ok || r.right==0 || r.bottom==0 ) { + return; + } + + m_initialRescrollFlag = false; + + DWORD start, end; + SendMessage( EM_GETSEL, (WPARAM)&start, (LPARAM)&end ); + SendMessage( EM_SETSEL, (WPARAM)0, (LPARAM)0 ); + SendMessage( EM_SETSEL, (WPARAM)start, (LPARAM)end ); +} + + /************************************************************************ * WTextFieldPeer native methods */ diff --git a/src/windows/native/sun/windows/awt_TextField.h b/src/windows/native/sun/windows/awt_TextField.h index 177329cf8601fcdef602b6f5e70708a93b7325ca..a037adff795b3fd2b200a25257246b5a118f51ce 100644 --- a/src/windows/native/sun/windows/awt_TextField.h +++ b/src/windows/native/sun/windows/awt_TextField.h @@ -55,9 +55,14 @@ public: static void _SetEchoChar(void *param); protected: - void EditSetSel(CHARRANGE &cr); - void EditGetSel(CHARRANGE &cr); LONG EditGetCharFromPos(POINT& pt); + virtual void Reshape(int x, int y, int w, int h); + +private: + void EditSetSel(CHARRANGE &cr); + void initialRescroll(); + + bool m_initialRescrollFlag; }; #endif /* AWT_TEXTFIELD_H */ diff --git a/src/windows/native/sun/windows/awt_Window.h b/src/windows/native/sun/windows/awt_Window.h index 1e99c3f84a7f43cd95efd99d85d2c8f66234ba63..8ad2b0902b2d5ac7aa219f2c9911917daf982961 100644 --- a/src/windows/native/sun/windows/awt_Window.h +++ b/src/windows/native/sun/windows/awt_Window.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2009, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1996, 2010, 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 @@ -193,12 +193,17 @@ public: // Execute on Toolkit only. INLINE static LRESULT SynthesizeWmActivate(BOOL doActivate, HWND targetHWnd, HWND oppositeHWnd) { - if (::IsWindowVisible(targetHWnd)) { - return ::SendMessage(targetHWnd, WM_ACTIVATE, - MAKEWPARAM(doActivate ? WA_ACTIVE : WA_INACTIVE, FALSE), - (LPARAM) oppositeHWnd); + if (doActivate && + (!::IsWindowVisible(targetHWnd) || ::IsIconic(::GetAncestor(targetHWnd, GA_ROOT)))) + { + // The activation is rejected if either: + // - The toplevel is not visible + // - The toplevel (or its embedder) is minimised + return 1; } - return 1; // if not processed + return ::SendMessage(targetHWnd, WM_ACTIVATE, + MAKEWPARAM(doActivate ? WA_ACTIVE : WA_INACTIVE, FALSE), + (LPARAM) oppositeHWnd); } void moveToDefaultLocation(); /* moves Window to X,Y specified by Window Manger */ diff --git a/test/java/awt/EventQueue/SecondaryLoopTest/SecondaryLoopTest.java b/test/java/awt/EventQueue/SecondaryLoopTest/SecondaryLoopTest.java new file mode 100644 index 0000000000000000000000000000000000000000..9dd251222a5233e2464f80ed398a33c5b447896d --- /dev/null +++ b/test/java/awt/EventQueue/SecondaryLoopTest/SecondaryLoopTest.java @@ -0,0 +1,126 @@ +/* + * Copyright (c) 2010, 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 6949936 + @author Artem Ananiev: area=eventqueue + @run main/timeout=30 SecondaryLoopTest +*/ + +import java.awt.*; + +/** + * Unit test for java.awt.SecondaryLoop implementation + */ +public class SecondaryLoopTest { + + private static volatile boolean loopStarted; + private static volatile boolean doubleEntered; + private static volatile boolean loopActive; + private static volatile boolean eventDispatched; + + public static void main(String[] args) throws Exception { + test(true, true); + test(true, false); + test(false, true); + test(false, false); + } + + private static void test(final boolean enterEDT, final boolean exitEDT) throws Exception { + System.out.println("Running test(" + enterEDT + ", " + exitEDT + ")"); + System.err.flush(); + loopStarted = true; + Runnable enterRun = new Runnable() { + @Override + public void run() { + Toolkit tk = Toolkit.getDefaultToolkit(); + EventQueue eq = tk.getSystemEventQueue(); + final SecondaryLoop loop = eq.createSecondaryLoop(); + doubleEntered = false; + eventDispatched = false; + Runnable eventRun = new Runnable() { + @Override + public void run() { + // Let the loop enter + sleep(1000); + if (loop.enter()) { + doubleEntered = true; + } + eventDispatched = true; + } + }; + EventQueue.invokeLater(eventRun); + Runnable exitRun = new Runnable() { + @Override + public void run() { + // Let the loop enter and eventRun finish + sleep(2000); + if (doubleEntered) { + // Hopefully, we get here if the loop is entered twice + loop.exit(); + } + loop.exit(); + } + }; + if (exitEDT) { + EventQueue.invokeLater(exitRun); + } else { + new Thread(exitRun).start(); + } + if (!loop.enter()) { + loopStarted = false; + } + loopActive = eventDispatched; + } + }; + if (enterEDT) { + EventQueue.invokeAndWait(enterRun); + } else { + enterRun.run(); + } + // Print all the flags before we fail with exception + System.out.println(" loopStarted = " + loopStarted); + System.out.println(" doubleEntered = " + doubleEntered); + System.out.println(" loopActive = " + loopActive); + System.out.flush(); + if (!loopStarted) { + throw new RuntimeException("Test FAILED: the secondary loop is not started"); + } + if (doubleEntered) { + throw new RuntimeException("Test FAILED: the secondary loop is started twice"); + } + if (!loopActive) { + throw new RuntimeException("Test FAILED: the secondary loop exited immediately"); + } + } + + private static void sleep(long t) { + try { + Thread.sleep(t); + } catch (InterruptedException e) { + e.printStackTrace(System.err); + } + } + +} diff --git a/test/java/awt/Menu/NullMenuLabelTest/NullMenuLabelTest.java b/test/java/awt/Menu/NullMenuLabelTest/NullMenuLabelTest.java new file mode 100644 index 0000000000000000000000000000000000000000..e779f40aab2676811b6e4e89c0ca84f828d1da6e --- /dev/null +++ b/test/java/awt/Menu/NullMenuLabelTest/NullMenuLabelTest.java @@ -0,0 +1,26 @@ +/* @test 1.5 98/07/23 + @bug 4064202 4253466 + @summary Test for Win32 NPE when MenuItem with null label added. + @author fred.ecks + @run main/othervm NullMenuLabelTest +*/ + +import java.awt.*; + +public class NullMenuLabelTest { + + public static void main(String[] args) { + Frame frame = new Frame("Test Frame"); + frame.pack(); + frame.setVisible(true); + MenuBar menuBar = new MenuBar(); + frame.setMenuBar(menuBar); + Menu menu = new Menu(null); + menuBar.add(menu); + menu.add(new MenuItem(null)); + // If we got this far, the test succeeded + frame.setVisible(false); + frame.dispose(); + } + +} // class NullMenuLabelTest diff --git a/test/java/awt/TextField/ScrollSelectionTest/ScrollSelectionTest.java b/test/java/awt/TextField/ScrollSelectionTest/ScrollSelectionTest.java index 9f9073fb590d45ea63d0c579a73dc79be3d9ef38..fb9df2cedabb46283d1bf1118b4bf4b396eb6c2c 100644 --- a/test/java/awt/TextField/ScrollSelectionTest/ScrollSelectionTest.java +++ b/test/java/awt/TextField/ScrollSelectionTest/ScrollSelectionTest.java @@ -53,13 +53,14 @@ public class ScrollSelectionTest extends Applet frame.add(tf); tf.select(0, 20); - String[] instructions = - { + String[] instructions = { "INSTRUCTIONS:", "This is a test for a win32 specific problem", - "If you see all the letters from 'a' to 'z' and", - "letters from 'a' to 't' are selected then test passes" - }; + "If you see all the letters from 'a' to 'z' and", + "letters from 'a' to 't' are selected then test passes.", + "You may have to activate the frame to see the selection" + + " highlighted (e.g. by clicking on frame's title)." + }; Sysout.createDialogWithInstructions( instructions ); }// init() diff --git a/test/java/awt/event/MouseEvent/SpuriousExitEnter/SpuriousExitEnter_3.java b/test/java/awt/event/MouseEvent/SpuriousExitEnter/SpuriousExitEnter_3.java index 2b03496b850507e4610f31b437cdc730a1a64dd8..d2de08e0f729d35dda09424142c3701546739cae 100644 --- a/test/java/awt/event/MouseEvent/SpuriousExitEnter/SpuriousExitEnter_3.java +++ b/test/java/awt/event/MouseEvent/SpuriousExitEnter/SpuriousExitEnter_3.java @@ -114,6 +114,7 @@ public class SpuriousExitEnter_3 { checkEvents(frameAdapter, 1, 1); checkEvents(buttonAdapter, 0, 0); w.setVisible(false); + Util.waitForIdle(r); } public static void main(String []s)