diff --git a/AutoComplete/src/main/java/org/fife/ui/autocomplete/AutoCompletePopupWindow.java b/AutoComplete/src/main/java/org/fife/ui/autocomplete/AutoCompletePopupWindow.java index 3876266..3d3d21d 100644 --- a/AutoComplete/src/main/java/org/fife/ui/autocomplete/AutoCompletePopupWindow.java +++ b/AutoComplete/src/main/java/org/fife/ui/autocomplete/AutoCompletePopupWindow.java @@ -74,6 +74,14 @@ class AutoCompletePopupWindow extends JWindow implements CaretListener, */ private AutoCompleteDescWindow descWindow; + /** + * Whether the description window is currently toggled "on" when + * {@link AutoCompletion#getDescWindowVisibility()} is + * {@link DescWindowVisibility#ON_DEMAND}. Ignored for other visibility + * settings. + */ + private boolean descWindowVisibleOnDemand; + /** * The preferred size of the optional description window. This field * only exists because the user may (and usually will) set the size of @@ -271,6 +279,115 @@ protected void doAutocomplete() { } + /** + * Returns whether the description window should currently be displayed, + * per {@link AutoCompletion#getDescWindowVisibility()}. + * + * @return Whether the description window should currently be displayed. + * @see #toggleDescriptionWindow() + */ + private boolean shouldShowDescWindow() { + switch (ac.getDescWindowVisibility()) { + case ALWAYS: + return true; + case ON_DEMAND: + return descWindowVisibleOnDemand; + case NEVER: + default: + return false; + } + } + + + /** + * Toggles whether the description window is displayed, when + * {@link AutoCompletion#getDescWindowVisibility()} is + * {@link DescWindowVisibility#ON_DEMAND}. Does nothing otherwise, or if + * this popup window is not currently visible. + */ + void toggleDescriptionWindow() { + + if (ac.getDescWindowVisibility() != DescWindowVisibility.ON_DEMAND || !isVisible()) { + return; + } + + descWindowVisibleOnDemand = !descWindowVisibleOnDemand; + + if (descWindowVisibleOnDemand) { + if (descWindow == null) { + descWindow = createDescriptionWindow(); + } + Completion c = list.getSelectedValue(); + if (c != null) { + descWindow.setDescriptionFor(c); + } + positionDescWindow(); + descWindow.setVisible(true); + } + else if (descWindow != null) { + descWindow.setVisible(false); + } + + } + + + /** + * The key used in the input map for the description window toggle action. + */ + private static final String DESC_WINDOW_TOGGLE_KEY = "AutoCompletion.ToggleDescWindow"; + + + /** + * Installs a "description window toggle key" action onto a text component. + * + * @param ac The auto-completion instance the text component is installed on. + * @param tc The text component. + * @param ks The keystroke that should toggle the description window's visibility. + * @see #uninstallDescWindowToggleKey(JTextComponent, KeyStroke) + */ + static void installDescWindowToggleKey(AutoCompletion ac, JTextComponent tc, KeyStroke ks) { + InputMap im = tc.getInputMap(); + im.put(ks, DESC_WINDOW_TOGGLE_KEY); + ActionMap am = tc.getActionMap(); + am.put(DESC_WINDOW_TOGGLE_KEY, new ToggleDescWindowAction(ac)); + } + + + /** + * Removes a previously-installed "description window toggle key" action from a text component. + * + * @param tc The text component. + * @param ks The keystroke previously passed to {@link #installDescWindowToggleKey}. + */ + static void uninstallDescWindowToggleKey(JTextComponent tc, KeyStroke ks) { + tc.getInputMap().remove(ks); + tc.getActionMap().remove(DESC_WINDOW_TOGGLE_KEY); + } + + + /** + * Toggles the description window's visibility when triggered while {@link DescWindowVisibility#ON_DEMAND} + * is active; a no-op otherwise. + */ + private static final class ToggleDescWindowAction extends AbstractAction { + + private final AutoCompletion ac; + + ToggleDescWindowAction(AutoCompletion ac) { + this.ac = ac; + } + + @Override + public void actionPerformed(ActionEvent e) { + AutoCompletePopupWindow popupWindow = ac.getPopupWindow(); + if (popupWindow != null) { + popupWindow.toggleDescriptionWindow(); + } + } + + } + + /** * Returns the copy keystroke to use for this platform. * @@ -316,7 +433,7 @@ AutoCompleteDescWindow getDescWindow() { * that never gets un-mapped or repainted. Disposing of the native peer * avoids that. * - * @see AutoCompletion#setShowDescWindow(boolean) + * @see AutoCompletion#setDescWindowVisibility(DescWindowVisibility) */ void disposeDescWindow() { if (descWindow != null) { @@ -452,7 +569,7 @@ public void mouseReleased(MouseEvent e) { */ private void positionDescWindow() { - boolean showDescWindow = descWindow!=null && ac.getShowDescWindow(); + boolean showDescWindow = descWindow!=null && shouldShowDescWindow(); if (!showDescWindow) { return; } @@ -703,7 +820,7 @@ public void setLocationRelativeTo(Rectangle r) { Rectangle screenBounds = Util.getScreenBoundsForPoint(r.x, r.y); //Dimension screenSize = getToolkit().getScreenSize(); - boolean showDescWindow = descWindow!=null && ac.getShowDescWindow(); + boolean showDescWindow = descWindow!=null && shouldShowDescWindow(); int totalH = getHeight(); if (showDescWindow) { totalH = Math.max(totalH, descWindow.getHeight()); @@ -755,7 +872,9 @@ public void setVisible(boolean visible) { installKeyBindings(); lastLine = ac.getLineOfCaret(); selectFirstItem(); - if (descWindow==null && ac.getShowDescWindow()) { + // ON_DEMAND starts back off each time the popup is (re)shown. + descWindowVisibleOnDemand = false; + if (descWindow==null && shouldShowDescWindow()) { descWindow = createDescriptionWindow(); positionDescWindow(); } @@ -771,6 +890,19 @@ public void setVisible(boolean visible) { } else { uninstallKeyBindings(); + // Explicitly hide the desc window *before* hiding ourselves. + // java.awt.Window#hide() cascades to any owned window that is + // still visible at that moment, hiding it too and flagging it + // to be automatically re-shown (via Window#show()'s internal + // "showWithParent" bookkeeping) the next time we're shown + // again - even if that desc window gets disposed in the + // meantime. Hiding it first ensures it's already invisible + // when our own super.setVisible(false) cascades below, so the + // JDK never sets that flag and can't resurrect a disposed + // desc window behind our back. + if (descWindow != null) { + descWindow.setVisible(false); + } } super.setVisible(visible); @@ -797,7 +929,7 @@ public void setVisible(boolean visible) { // because of the way child JWindows' visibility is handled - in // some ways it's dependent on the parent, in other ways it's not. if (descWindow!=null) { - descWindow.setVisible(visible && ac.getShowDescWindow()); + descWindow.setVisible(visible && shouldShowDescWindow()); } } diff --git a/AutoComplete/src/main/java/org/fife/ui/autocomplete/AutoCompletion.java b/AutoComplete/src/main/java/org/fife/ui/autocomplete/AutoCompletion.java index a2a7032..318741e 100644 --- a/AutoComplete/src/main/java/org/fife/ui/autocomplete/AutoCompletion.java +++ b/AutoComplete/src/main/java/org/fife/ui/autocomplete/AutoCompletion.java @@ -107,10 +107,10 @@ public class AutoCompletion { private static LinkRedirector linkRedirector; /** - * Whether the description window should be displayed along with the + * Whether/when the description window should be displayed along with the * completion choice window. */ - private boolean showDescWindow; + private DescWindowVisibility descWindowVisibility; /** * Whether auto-complete is enabled. @@ -176,6 +176,12 @@ public class AutoCompletion { */ private Action oldParenAction; + /** + * The keystroke that toggles the description window's visibility in + * {@link DescWindowVisibility#ON_DEMAND} mode, or null. + */ + private KeyStroke descWindowToggleKey; + /** * Listens for events in the parent window that affect the visibility of the * popup windows. @@ -268,7 +274,8 @@ public AutoCompletion(CompletionProvider provider) { setAutoCompleteEnabled(true); setAutoCompleteSingleChoices(true); setAutoActivationEnabled(false); - setShowDescWindow(false); + setDescWindowVisibility(DescWindowVisibility.NEVER); + setDescWindowToggleKey(getDefaultDescWindowToggleKey()); setHideOnCompletionProviderChange(true); setHideOnNoText(true); setParameterDescriptionTruncateThreshold(300); @@ -384,6 +391,18 @@ public static KeyStroke getDefaultTriggerKey() { } + /** + * Returns the default desc window toggle keystroke ({@link DescWindowVisibility#ON_DEMAND}). + * + * @return The default keystroke. + * @see #setDescWindowToggleKey(KeyStroke) + */ + public static KeyStroke getDefaultDescWindowToggleKey() { + int mask = InputEvent.CTRL_DOWN_MASK | InputEvent.SHIFT_DOWN_MASK; + return KeyStroke.getKeyStroke(KeyEvent.VK_SPACE, mask); + } + + /** * Returns the handler to use when an external URL is clicked in the * description window. @@ -462,14 +481,24 @@ protected String getReplacementText(Completion c, Document doc, int start, /** - * Returns whether the "description window" should be shown alongside the - * completion window. + * Returns whether/when the "description window" should be shown alongside the completion choices window. * - * @return Whether the description window should be shown. - * @see #setShowDescWindow(boolean) + * @return Whether/when the description window should be shown. + * @see #setDescWindowVisibility(DescWindowVisibility) */ - public boolean getShowDescWindow() { - return showDescWindow; + public DescWindowVisibility getDescWindowVisibility() { + return descWindowVisibility; + } + + + /** + * Returns the desc window toggle keystroke ({@link DescWindowVisibility#ON_DEMAND}). + * + * @return The keystroke, or null if none is installed. + * @see #setDescWindowToggleKey(KeyStroke) + */ + public KeyStroke getDescWindowToggleKey() { + return descWindowToggleKey; } @@ -665,6 +694,9 @@ public void install(JTextComponent c) { this.textComponent = c; installTriggerKey(getTriggerKey()); + if (descWindowToggleKey != null) { + AutoCompletePopupWindow.installDescWindowToggleKey(this, textComponent, descWindowToggleKey); + } // Install the function completion key, if there is one. // NOTE: We cannot do this if the start char is ' ' (e.g. just a space @@ -1148,20 +1180,20 @@ protected void setPopupVisible(boolean visible) { /** - * Sets whether the "description window" should be shown beside the - * completion window. + * Sets whether/when the "description window" should be shown beside the completion choices window. * - * @param show Whether to show the description window. - * @see #getShowDescWindow() + * @param visibility Whether/when to show the description window. + * @see #getDescWindowVisibility() */ - public void setShowDescWindow(boolean show) { + public void setDescWindowVisibility(DescWindowVisibility visibility) { + Objects.requireNonNull(visibility, "visibility cannot be null"); hidePopupWindow(); // Needed to force it to take effect - if (!show && popupWindow != null) { + if (visibility != DescWindowVisibility.ALWAYS && popupWindow != null) { // Dispose (rather than hide) the desc window on toggle-off, to avoid a // Linux/X11 "ghost" window bug when hiding it instead; see issue #84. popupWindow.disposeDescWindow(); } - showDescWindow = show; + descWindowVisibility = visibility; } @@ -1186,6 +1218,27 @@ public void setTriggerKey(KeyStroke ks) { } + /** + * Sets the desc window toggle keystroke ({@link DescWindowVisibility#ON_DEMAND}). + * + * @param ks The keystroke, or {@code null} to remove any previously installed toggle keystroke. + * @see #getDescWindowToggleKey() + */ + public void setDescWindowToggleKey(KeyStroke ks) { + if (!Objects.equals(ks, descWindowToggleKey)) { + if (textComponent != null) { + if (descWindowToggleKey != null) { + AutoCompletePopupWindow.uninstallDescWindowToggleKey(textComponent, descWindowToggleKey); + } + if (ks != null) { + AutoCompletePopupWindow.installDescWindowToggleKey(this, textComponent, ks); + } + } + descWindowToggleKey = ks; + } + } + + /** * Displays a "tool tip" detailing the inputs to the function just entered. * @@ -1238,6 +1291,9 @@ public void uninstall() { hidePopupWindow(); // Unregisters listeners, actions, etc. uninstallTriggerKey(); + if (descWindowToggleKey != null) { + AutoCompletePopupWindow.uninstallDescWindowToggleKey(textComponent, descWindowToggleKey); + } // Uninstall the function completion key. char start = provider.getParameterListStart(); diff --git a/AutoComplete/src/main/java/org/fife/ui/autocomplete/DescWindowVisibility.java b/AutoComplete/src/main/java/org/fife/ui/autocomplete/DescWindowVisibility.java new file mode 100644 index 0000000..575a191 --- /dev/null +++ b/AutoComplete/src/main/java/org/fife/ui/autocomplete/DescWindowVisibility.java @@ -0,0 +1,38 @@ +/* + * This library is distributed under a modified BSD license. See the included + * LICENSE.md file for details. + */ +package org.fife.ui.autocomplete; + + +/** + * Controls when the "description" window (the popup that shows documentation + * for the currently selected completion choice) is displayed alongside the + * completion choices window. + * + * @author Robert Futrell + * @version 1.0 + * @see AutoCompletion#setDescWindowVisibility(DescWindowVisibility) + */ +public enum DescWindowVisibility { + + /** + * The description window is shown automatically whenever the completion + * choices window is showing and a description is available. This is the + * default (legacy) behavior. + */ + ALWAYS, + + /** + * The description window is only shown when the user explicitly requests + * it, via the keystroke configured by + * {@link AutoCompletion#setDescWindowToggleKey(javax.swing.KeyStroke)}. + */ + ON_DEMAND, + + /** + * The description window is never shown. + */ + NEVER + +} diff --git a/AutoComplete/src/test/java/org/fife/ui/autocomplete/AutoCompletionTest.java b/AutoComplete/src/test/java/org/fife/ui/autocomplete/AutoCompletionTest.java index 003fffb..ad7d098 100644 --- a/AutoComplete/src/test/java/org/fife/ui/autocomplete/AutoCompletionTest.java +++ b/AutoComplete/src/test/java/org/fife/ui/autocomplete/AutoCompletionTest.java @@ -5,8 +5,13 @@ package org.fife.ui.autocomplete; import java.awt.GraphicsEnvironment; +import java.awt.event.ActionEvent; +import javax.swing.Action; +import javax.swing.ActionMap; +import javax.swing.InputMap; import javax.swing.JFrame; import javax.swing.JTextArea; +import javax.swing.KeyStroke; import javax.swing.text.DefaultCaret; import javax.swing.text.JTextComponent; @@ -55,7 +60,7 @@ void setShowDescWindow_false_disposesExistingDescWindow() { provider.addCompletion(new BasicCompletion(provider, "foobar", "foobar's summary")); AutoCompletion ac = new AutoCompletion(provider); - ac.setShowDescWindow(true); + ac.setDescWindowVisibility(DescWindowVisibility.ALWAYS); ac.setAutoCompleteEnabled(true); JTextArea textArea = new JTextArea(); @@ -79,7 +84,7 @@ void setShowDescWindow_false_disposesExistingDescWindow() { Assertions.assertTrue(descWindow.isDisplayable(), "Description window's native peer should exist while showing"); - ac.setShowDescWindow(false); + ac.setDescWindowVisibility(DescWindowVisibility.NEVER); Assertions.assertNull(popupWindow.getDescWindow(), "Description window should be disposed and discarded, not just hidden"); @@ -89,6 +94,262 @@ void setShowDescWindow_false_disposesExistingDescWindow() { } + @Test + void getShowDescWindow_defaultsToNever() { + AutoCompletion ac = new AutoCompletion(new DefaultCompletionProvider()); + Assertions.assertEquals(DescWindowVisibility.NEVER, ac.getDescWindowVisibility()); + } + + + /** + * Regression test for a bug where a disposed description window could be + * silently resurrected by the JDK. {@code java.awt.Window#hide()} has an + * internal cascade: any owned window that's still visible when its owner + * is hidden gets hidden too and flagged (via a package-private + * {@code showWithParent} field) to be automatically re-shown - by calling + * {@code show()} directly, bypassing all of our code - the next time the + * owner is shown again, even if that owned window was disposed of in the + * meantime. If the description window is still visible at the moment the + * choices popup is hidden (e.g. the text component loses focus), that + * flag gets set; disposing the description window afterward (by + * switching to {@code NEVER}) doesn't clear it, so the next time the + * choices popup reopens, the JDK recreates the disposed window's native + * peer and shows it again with its last (now stale) content - even + * though {@code AutoCompletePopupWindow}'s own {@code descWindow} field + * is {@code null}. The fix hides the description window *before* hiding + * the choices popup, so it's already invisible by the time the JDK's + * cascade runs and never sets that flag. + */ + @Test + void showDescWindow_disposedWhileHidden_doesNotReappearOnNextShow() { + + DefaultCompletionProvider provider = new DefaultCompletionProvider(); + provider.addCompletion(new BasicCompletion(provider, "foo", "foo's summary")); + provider.addCompletion(new BasicCompletion(provider, "foobar", "foobar's summary")); + + AutoCompletion ac = new AutoCompletion(provider); + ac.setDescWindowVisibility(DescWindowVisibility.ALWAYS); + ac.setAutoCompleteEnabled(true); + + JTextArea textArea = new JTextArea(); + ac.install(textArea); + + frame = new JFrame(); + frame.add(textArea); + frame.pack(); + frame.setVisible(true); + + textArea.setText("foo"); + textArea.setCaretPosition(textArea.getText().length()); + ac.doCompletion(); + + AutoCompletePopupWindow popupWindow = ac.getPopupWindow(); + AutoCompleteDescWindow descWindow = popupWindow.getDescWindow(); + Assertions.assertNotNull(descWindow); + Assertions.assertTrue(descWindow.isVisible()); + + // Hide the choices popup while the desc window is still visible, as + // happens e.g. when the text component loses focus. This is what + // causes the JDK to internally flag the desc window to be + // auto-restored the next time the popup is shown again. + popupWindow.setVisible(false); + + // Now dispose the desc window entirely, as setDescWindowVisibility() + // does when switching away from ALWAYS. + ac.setDescWindowVisibility(DescWindowVisibility.NEVER); + Assertions.assertNull(popupWindow.getDescWindow()); + Assertions.assertFalse(descWindow.isDisplayable()); + + // Re-trigger completion; the choices popup reopens, but the disposed + // desc window must not be silently resurrected by the JDK. + ac.doCompletion(); + + Assertions.assertNull(popupWindow.getDescWindow(), + "No new description window should have been created (NEVER)"); + Assertions.assertFalse(descWindow.isVisible(), + "The disposed description window must not be resurrected by the JDK"); + Assertions.assertFalse(descWindow.isDisplayable(), + "The disposed description window's peer must not be silently recreated"); + + } + + + @Test + void showDescWindow_never_descWindowNeverCreated() { + + DefaultCompletionProvider provider = new DefaultCompletionProvider(); + provider.addCompletion(new BasicCompletion(provider, "foo", "foo's summary")); + provider.addCompletion(new BasicCompletion(provider, "foobar", "foobar's summary")); + + AutoCompletion ac = new AutoCompletion(provider); + ac.setDescWindowVisibility(DescWindowVisibility.NEVER); + ac.setAutoCompleteEnabled(true); + + JTextArea textArea = new JTextArea(); + ac.install(textArea); + + frame = new JFrame(); + frame.add(textArea); + frame.pack(); + frame.setVisible(true); + + textArea.setText("foo"); + textArea.setCaretPosition(textArea.getText().length()); + ac.doCompletion(); + + AutoCompletePopupWindow popupWindow = ac.getPopupWindow(); + Assertions.assertNotNull(popupWindow, "Choices popup window should still be shown"); + Assertions.assertNull(popupWindow.getDescWindow(), + "Description window should never be created when visibility is NEVER"); + + } + + + @Test + void showDescWindow_onDemand_toggleKeyShowsAndHidesDescWindow() { + + DefaultCompletionProvider provider = new DefaultCompletionProvider(); + provider.addCompletion(new BasicCompletion(provider, "foo", "foo's summary")); + provider.addCompletion(new BasicCompletion(provider, "foobar", "foobar's summary")); + + AutoCompletion ac = new AutoCompletion(provider); + ac.setDescWindowVisibility(DescWindowVisibility.ON_DEMAND); + ac.setAutoCompleteEnabled(true); + + JTextArea textArea = new JTextArea(); + ac.install(textArea); + + frame = new JFrame(); + frame.add(textArea); + frame.pack(); + frame.setVisible(true); + + textArea.setText("foo"); + textArea.setCaretPosition(textArea.getText().length()); + ac.doCompletion(); + + AutoCompletePopupWindow popupWindow = ac.getPopupWindow(); + Assertions.assertNotNull(popupWindow); + Assertions.assertNull(popupWindow.getDescWindow(), + "Description window should not be created until toggled on, in ON_DEMAND mode"); + + fireKeyAction(textArea, ac.getDescWindowToggleKey()); + AutoCompleteDescWindow descWindow = popupWindow.getDescWindow(); + Assertions.assertNotNull(descWindow, + "Description window should be created the first time it's toggled on"); + Assertions.assertTrue(descWindow.isVisible(), + "Description window should be visible after toggling on"); + + fireKeyAction(textArea, ac.getDescWindowToggleKey()); + Assertions.assertFalse(descWindow.isVisible(), + "Description window should be hidden after toggling back off"); + + } + + + @Test + void showDescWindow_onDemand_toggleKeyIgnoredWhenNotOnDemand() { + + DefaultCompletionProvider provider = new DefaultCompletionProvider(); + provider.addCompletion(new BasicCompletion(provider, "foo", "foo's summary")); + provider.addCompletion(new BasicCompletion(provider, "foobar", "foobar's summary")); + + AutoCompletion ac = new AutoCompletion(provider); + ac.setDescWindowVisibility(DescWindowVisibility.ALWAYS); + ac.setAutoCompleteEnabled(true); + + JTextArea textArea = new JTextArea(); + ac.install(textArea); + + frame = new JFrame(); + frame.add(textArea); + frame.pack(); + frame.setVisible(true); + + textArea.setText("foo"); + textArea.setCaretPosition(textArea.getText().length()); + ac.doCompletion(); + + AutoCompletePopupWindow popupWindow = ac.getPopupWindow(); + AutoCompleteDescWindow descWindow = popupWindow.getDescWindow(); + Assertions.assertNotNull(descWindow, "Description window should already be shown (ALWAYS)"); + + fireKeyAction(textArea, ac.getDescWindowToggleKey()); + Assertions.assertTrue(descWindow.isVisible(), + "Toggle key should have no effect when visibility isn't ON_DEMAND"); + + } + + + @Test + void setDescWindowToggleKey_customKeystrokeIsInstalledAndUsable() { + + DefaultCompletionProvider provider = new DefaultCompletionProvider(); + provider.addCompletion(new BasicCompletion(provider, "foo", "foo's summary")); + provider.addCompletion(new BasicCompletion(provider, "foobar", "foobar's summary")); + + AutoCompletion ac = new AutoCompletion(provider); + ac.setDescWindowVisibility(DescWindowVisibility.ON_DEMAND); + ac.setAutoCompleteEnabled(true); + + KeyStroke customKey = KeyStroke.getKeyStroke("ctrl alt D"); + ac.setDescWindowToggleKey(customKey); + + JTextArea textArea = new JTextArea(); + ac.install(textArea); + + frame = new JFrame(); + frame.add(textArea); + frame.pack(); + frame.setVisible(true); + + textArea.setText("foo"); + textArea.setCaretPosition(textArea.getText().length()); + ac.doCompletion(); + + AutoCompletePopupWindow popupWindow = ac.getPopupWindow(); + Assertions.assertNull(popupWindow.getDescWindow()); + + fireKeyAction(textArea, customKey); + Assertions.assertNotNull(popupWindow.getDescWindow(), + "Description window should toggle on via the custom keystroke"); + + } + + + @Test + void setDescWindowToggleKey_null_removesInstalledKeystroke() { + + AutoCompletion ac = new AutoCompletion(new DefaultCompletionProvider()); + JTextArea textArea = new JTextArea(); + ac.install(textArea); + + KeyStroke defaultKey = AutoCompletion.getDefaultDescWindowToggleKey(); + InputMap im = textArea.getInputMap(); + Assertions.assertNotNull(im.get(defaultKey), + "Default toggle key should be installed on install()"); + + ac.setDescWindowToggleKey(null); + Assertions.assertNull(ac.getDescWindowToggleKey()); + + } + + + /** + * Fires the action bound to {@code ks} in {@code textArea}'s input/action maps, + * simulating the user pressing that keystroke. + */ + private static void fireKeyAction(JTextArea textArea, KeyStroke ks) { + InputMap im = textArea.getInputMap(); + ActionMap am = textArea.getActionMap(); + Object key = im.get(ks); + Assertions.assertNotNull(key, "No action bound to keystroke " + ks); + Action action = am.get(key); + Assertions.assertNotNull(action, "No action found for key " + key); + action.actionPerformed(new ActionEvent(textArea, ActionEvent.ACTION_PERFORMED, "")); + } + + /** * Regression test for Issue #77 - * {@code isAutoActivateOkay()} used to look at {@code JTextComponent#getCaretPosition()} diff --git a/AutoCompleteDemo/src/main/java/org/fife/ui/autocomplete/demo/DemoRootPane.java b/AutoCompleteDemo/src/main/java/org/fife/ui/autocomplete/demo/DemoRootPane.java index 9d7889b..dc691e4 100644 --- a/AutoCompleteDemo/src/main/java/org/fife/ui/autocomplete/demo/DemoRootPane.java +++ b/AutoCompleteDemo/src/main/java/org/fife/ui/autocomplete/demo/DemoRootPane.java @@ -90,7 +90,7 @@ class DemoRootPane extends JRootPane { // Install auto-completion onto our text area. ac = new AutoCompletion(provider); ac.setListCellRenderer(new CCellRenderer()); - ac.setShowDescWindow(true); + ac.setDescWindowVisibility(DescWindowVisibility.ALWAYS); ac.setParameterAssistanceEnabled(true); ac.setAutoCompleteEnabled(true); @@ -449,7 +449,7 @@ private class ShowDescWindowAction extends AbstractAction { @Override public void actionPerformed(ActionEvent e) { boolean show = showDescWindowItem.isSelected(); - ac.setShowDescWindow(show); + ac.setDescWindowVisibility(show ? DescWindowVisibility.ALWAYS : DescWindowVisibility.NEVER); } } diff --git a/config/checkstyle/checkstyle.xml b/config/checkstyle/checkstyle.xml index b5fbb99..503627a 100644 --- a/config/checkstyle/checkstyle.xml +++ b/config/checkstyle/checkstyle.xml @@ -64,7 +64,7 @@ - + diff --git a/gradle.properties b/gradle.properties index 4b1635a..4f708d1 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,6 +1,6 @@ # Note that Maven- and signing-related properties are in /gradle.properties javaLanguageVersion=11 -version=3.3.4-SNAPSHOT +version=4.0.0-SNAPSHOT # Ugh, see https://github.com/gradle/gradle/issues/11308 systemProp.org.gradle.internal.publish.checksums.insecure=true