diff --git a/designer-base/src/main/java/com/fr/design/formula/FormulaPane.java b/designer-base/src/main/java/com/fr/design/formula/FormulaPane.java
index 904cdf00b..da61fab15 100644
--- a/designer-base/src/main/java/com/fr/design/formula/FormulaPane.java
+++ b/designer-base/src/main/java/com/fr/design/formula/FormulaPane.java
@@ -16,6 +16,7 @@ import com.fr.design.dialog.BasicPane;
import com.fr.design.dialog.DialogActionAdapter;
import com.fr.design.dialog.FineJOptionPane;
import com.fr.design.file.HistoryTemplateListCache;
+import com.fr.design.gui.autocomplete.AutoCompleteExtraRefreshComponent;
import com.fr.design.gui.autocomplete.CompletionCellRenderer;
import com.fr.design.gui.autocomplete.CompletionProvider;
import com.fr.design.gui.autocomplete.DefaultCompletionProvider;
@@ -416,7 +417,7 @@ public class FormulaPane extends BasicPane implements KeyListener, UIFormula {
autoCompletion = new FormulaPaneAutoCompletion(provider);
autoCompletion.setListCellRenderer(new CompletionCellRenderer());
autoCompletion.install(formulaTextArea);
- autoCompletion.installVariableTree(variableTreeAndDescriptionArea);
+ autoCompletion.installExtraRefreshComponent(variableTreeAndDescriptionArea);
}
@@ -1036,7 +1037,7 @@ public class FormulaPane extends BasicPane implements KeyListener, UIFormula {
}
}
- public class VariableTreeAndDescriptionArea extends JPanel {
+ public class VariableTreeAndDescriptionArea extends JPanel implements AutoCompleteExtraRefreshComponent {
private JTree variablesTree;
private UITextArea descriptionTextArea;
@@ -1277,6 +1278,11 @@ public class FormulaPane extends BasicPane implements KeyListener, UIFormula {
functionTypeList.setSelectedIndex(0);
}
+ @Override
+ public void refresh(String replacementText) {
+ refreshText(replacementText);
+ }
+
/*
* 查看函数的详细信息
*/
diff --git a/designer-base/src/main/java/com/fr/design/gui/autocomplete/AutoCompleteExtraRefreshComponent.java b/designer-base/src/main/java/com/fr/design/gui/autocomplete/AutoCompleteExtraRefreshComponent.java
new file mode 100644
index 000000000..ff6b7faef
--- /dev/null
+++ b/designer-base/src/main/java/com/fr/design/gui/autocomplete/AutoCompleteExtraRefreshComponent.java
@@ -0,0 +1,5 @@
+package com.fr.design.gui.autocomplete;
+
+public interface AutoCompleteExtraRefreshComponent {
+ void refresh(String replacementText);
+}
diff --git a/designer-base/src/main/java/com/fr/design/gui/autocomplete/AutoCompleteWithExtraRefreshPopupWindow.java b/designer-base/src/main/java/com/fr/design/gui/autocomplete/AutoCompleteWithExtraRefreshPopupWindow.java
new file mode 100644
index 000000000..3f4f19177
--- /dev/null
+++ b/designer-base/src/main/java/com/fr/design/gui/autocomplete/AutoCompleteWithExtraRefreshPopupWindow.java
@@ -0,0 +1,982 @@
+package com.fr.design.gui.autocomplete;
+
+import com.fr.design.gui.syntax.ui.rsyntaxtextarea.PopupWindowDecorator;
+import com.fr.log.FineLoggerFactory;
+import java.awt.BorderLayout;
+import java.awt.ComponentOrientation;
+import java.awt.Dimension;
+import java.awt.Point;
+import java.awt.Rectangle;
+import java.awt.Toolkit;
+import java.awt.Window;
+import java.awt.event.ActionEvent;
+import java.awt.event.KeyEvent;
+import java.awt.event.MouseEvent;
+import java.awt.event.MouseListener;
+import java.util.List;
+import javax.swing.AbstractAction;
+import javax.swing.Action;
+import javax.swing.ActionMap;
+import javax.swing.InputMap;
+import javax.swing.JComponent;
+import javax.swing.JList;
+import javax.swing.JPanel;
+import javax.swing.JScrollPane;
+import javax.swing.JWindow;
+import javax.swing.KeyStroke;
+import javax.swing.ListCellRenderer;
+import javax.swing.SwingUtilities;
+import javax.swing.event.CaretEvent;
+import javax.swing.event.CaretListener;
+import javax.swing.event.ListSelectionEvent;
+import javax.swing.event.ListSelectionListener;
+import javax.swing.plaf.ListUI;
+import javax.swing.text.Caret;
+import javax.swing.text.JTextComponent;
+
+public abstract class AutoCompleteWithExtraRefreshPopupWindow extends JWindow implements CaretListener,
+ ListSelectionListener, MouseListener {
+ private final static int DIS = 5;
+ /**
+ * The parent AutoCompletion instance.
+ */
+ private AutoCompletion ac;
+
+ /**
+ * The list of completion choices.
+ */
+ private JList list;
+
+ /**
+ * The contents of {@link #list()}.
+ */
+ private CompletionListModel model;
+
+ /**
+ * A hack to work around the fact that we clear our completion model (and
+ * our selection) when hiding the completion window. This allows us to
+ * still know what the user selected after the popup is hidden.
+ */
+ private Completion lastSelection;
+
+ /**
+ * Optional popup window containing a description of the currently
+ * selected completion.
+ */
+ private AutoCompleteDescWindow descWindow;
+
+ /**
+ * The preferred size of the optional description window. This field
+ * only exists because the user may (and usually will) set the size of
+ * the description window before it exists (it must be parented to a
+ * Window).
+ */
+ private Dimension preferredDescWindowSize;
+
+
+
+ /**
+ * Whether the completion window and the optional description window
+ * should be displayed above the current caret position (as opposed to
+ * underneath it, which is preferred unless there is not enough space).
+ */
+ private boolean aboveCaret;
+
+ private int lastLine;
+
+ private KeyActionPair escapeKap;
+ private KeyActionPair upKap;
+ private KeyActionPair downKap;
+ private KeyActionPair leftKap;
+ private KeyActionPair rightKap;
+ private KeyActionPair enterKap;
+ private KeyActionPair tabKap;
+ private KeyActionPair homeKap;
+ private KeyActionPair endKap;
+ private KeyActionPair pageUpKap;
+ private KeyActionPair pageDownKap;
+ private KeyActionPair ctrlCKap;
+
+ private KeyActionPair oldEscape, oldUp, oldDown, oldLeft, oldRight,
+ oldEnter, oldTab, oldHome, oldEnd, oldPageUp, oldPageDown,
+ oldCtrlC;
+
+ /**
+ * The space between the caret and the completion popup.
+ */
+ private static final int VERTICAL_SPACE = 1;
+
+ /**
+ * The class name of the Substance List UI.
+ */
+ private static final String SUBSTANCE_LIST_UI =
+ "org.pushingpixels.substance.internal.ui.SubstanceListUI";
+
+
+ protected AutoCompleteExtraRefreshComponent component;
+
+
+ /**
+ * Constructor.
+ *
+ * @param parent The parent window (hosting the text component).
+ * @param ac The auto-completion instance.
+ */
+ public AutoCompleteWithExtraRefreshPopupWindow(Window parent, final AutoCompletion ac) {
+
+ super(parent);
+ ComponentOrientation o = ac.getTextComponentOrientation();
+
+ this.ac = ac;
+ model = new CompletionListModel();
+ list = new PopupList(model);
+
+ list.setCellRenderer(new DelegatingCellRenderer());
+ list.addListSelectionListener(this);
+ list.addMouseListener(this);
+
+ JPanel contentPane = new JPanel(new BorderLayout());
+ JScrollPane sp = new JScrollPane(list,
+ JScrollPane.VERTICAL_SCROLLBAR_ALWAYS,
+ JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS);
+
+ // In 1.4, JScrollPane.setCorner() has a bug where it won't accept
+ // JScrollPane.LOWER_TRAILING_CORNER, even though that constant is
+ // defined. So we have to put the logic added in 1.5 to handle it
+ // here.
+ JPanel corner = new SizeGrip();
+ //sp.setCorner(JScrollPane.LOWER_TRAILING_CORNER, corner);
+ boolean isLeftToRight = o.isLeftToRight();
+ String str = isLeftToRight ? JScrollPane.LOWER_RIGHT_CORNER :
+ JScrollPane.LOWER_LEFT_CORNER;
+ sp.setCorner(str, corner);
+
+ contentPane.add(sp);
+ setContentPane(contentPane);
+ applyComponentOrientation(o);
+
+ // Give apps a chance to decorate us with drop shadows, etc.
+ if (Util.getShouldAllowDecoratingMainAutoCompleteWindows()) {
+ PopupWindowDecorator decorator = PopupWindowDecorator.get();
+ if (decorator != null) {
+ decorator.decorate(this);
+ }
+ }
+
+ pack();
+
+ setFocusableWindowState(false);
+
+ lastLine = -1;
+
+ }
+
+
+ public void caretUpdate(CaretEvent e) {
+ if (isVisible()) { // Should always be true
+ int line = ac.getLineOfCaret();
+ if (line != lastLine) {
+ lastLine = -1;
+ setVisible(false);
+ } else {
+ doAutocomplete();
+ }
+ } else if (AutoCompletion.isDebug()) {
+ Thread.dumpStack();
+ }
+ }
+
+
+ /**
+ * Creates the description window.
+ *
+ * @return The description window.
+ */
+ private AutoCompleteDescWindow createDescriptionWindow() {
+ AutoCompleteDescWindow dw = new AutoCompleteDescWindow(this, ac);
+ dw.applyComponentOrientation(ac.getTextComponentOrientation());
+ Dimension size = preferredDescWindowSize;
+ if (size == null) {
+ size = getSize();
+ }
+ dw.setSize(size);
+ return dw;
+ }
+
+
+ /**
+ * Creates the mappings from keys to Actions we'll be putting into the
+ * text component's ActionMap and InputMap.
+ */
+ private void createKeyActionPairs() {
+
+ // Actions we'll install.
+ EnterAction enterAction = new EnterAction();
+ escapeKap = new KeyActionPair("Escape", new EscapeAction());
+ upKap = new KeyActionPair("Up", new UpAction());
+ downKap = new KeyActionPair("Down", new DownAction());
+ leftKap = new KeyActionPair("Left", new LeftAction());
+ rightKap = new KeyActionPair("Right", new RightAction());
+ enterKap = new KeyActionPair("Enter", enterAction);
+ tabKap = new KeyActionPair("Tab", enterAction);
+ homeKap = new KeyActionPair("Home", new HomeAction());
+ endKap = new KeyActionPair("End", new EndAction());
+ pageUpKap = new KeyActionPair("PageUp", new PageUpAction());
+ pageDownKap = new KeyActionPair("PageDown", new PageDownAction());
+ ctrlCKap = new KeyActionPair("CtrlC", new CopyAction());
+
+ // Buffers for the actions we replace.
+ oldEscape = new KeyActionPair();
+ oldUp = new KeyActionPair();
+ oldDown = new KeyActionPair();
+ oldLeft = new KeyActionPair();
+ oldRight = new KeyActionPair();
+ oldEnter = new KeyActionPair();
+ oldTab = new KeyActionPair();
+ oldHome = new KeyActionPair();
+ oldEnd = new KeyActionPair();
+ oldPageUp = new KeyActionPair();
+ oldPageDown = new KeyActionPair();
+ oldCtrlC = new KeyActionPair();
+
+ }
+
+
+ protected void doAutocomplete() {
+ lastLine = ac.refreshPopupWindow();
+ }
+
+
+ /**
+ * Returns the copy keystroke to use for this platform.
+ *
+ * @return The copy keystroke.
+ */
+ private static final KeyStroke getCopyKeyStroke() {
+ int key = KeyEvent.VK_C;
+ int mask = Toolkit.getDefaultToolkit().getMenuShortcutKeyMask();
+ return KeyStroke.getKeyStroke(key, mask);
+ }
+
+
+ /**
+ * Returns the default list cell renderer used when a completion provider
+ * does not supply its own.
+ *
+ * @return The default list cell renderer.
+ * @see #setListCellRenderer(ListCellRenderer)
+ */
+ public ListCellRenderer getListCellRenderer() {
+ DelegatingCellRenderer dcr = (DelegatingCellRenderer) list.
+ getCellRenderer();
+ return dcr.getFallbackCellRenderer();
+ }
+
+
+ /**
+ * Returns the selected value, or null
if nothing is selected.
+ *
+ * @return The selected value.
+ */
+ public Completion getSelection() {
+ return isShowing() ? (Completion) list.getSelectedValue() : lastSelection;
+ }
+
+
+ /**
+ * Inserts the currently selected completion.
+ *
+ * @see #getSelection()
+ */
+ private void insertSelectedCompletion() {
+ Completion comp = getSelection();
+ ac.insertCompletion(comp);
+ }
+
+ public void installComp(AutoCompleteExtraRefreshComponent component){
+ this.component = component;
+ }
+
+ public void refreshInstallComp() {
+ component.refresh(getSelection().getReplacementText());
+ }
+
+ /**
+ * Registers keyboard actions to listen for in the text component and
+ * intercept.
+ *
+ * @see #uninstallKeyBindings()
+ */
+ private void installKeyBindings() {
+
+ if (AutoCompletion.isDebug()) {
+ FineLoggerFactory.getLogger().debug("PopupWindow: Installing keybindings");
+ }
+
+ if (escapeKap == null) { // Lazily create actions.
+ createKeyActionPairs();
+ }
+
+ JTextComponent comp = ac.getTextComponent();
+ InputMap im = comp.getInputMap();
+ ActionMap am = comp.getActionMap();
+
+ replaceAction(im, am, KeyEvent.VK_ESCAPE, escapeKap, oldEscape);
+ if (AutoCompletion.isDebug() && oldEscape.action == escapeKap.action) {
+ Thread.dumpStack();
+ }
+ replaceAction(im, am, KeyEvent.VK_UP, upKap, oldUp);
+ replaceAction(im, am, KeyEvent.VK_LEFT, leftKap, oldLeft);
+ replaceAction(im, am, KeyEvent.VK_DOWN, downKap, oldDown);
+ replaceAction(im, am, KeyEvent.VK_RIGHT, rightKap, oldRight);
+ replaceAction(im, am, KeyEvent.VK_ENTER, enterKap, oldEnter);
+ replaceAction(im, am, KeyEvent.VK_TAB, tabKap, oldTab);
+ replaceAction(im, am, KeyEvent.VK_HOME, homeKap, oldHome);
+ replaceAction(im, am, KeyEvent.VK_END, endKap, oldEnd);
+ replaceAction(im, am, KeyEvent.VK_PAGE_UP, pageUpKap, oldPageUp);
+ replaceAction(im, am, KeyEvent.VK_PAGE_DOWN, pageDownKap, oldPageDown);
+
+ // Make Ctrl+C copy from description window. This isn't done
+ // automagically because the desc. window is not focusable, and copying
+ // from text components can only be done from focused components.
+ KeyStroke ks = getCopyKeyStroke();
+ oldCtrlC.key = im.get(ks);
+ im.put(ks, ctrlCKap.key);
+ oldCtrlC.action = am.get(ctrlCKap.key);
+ am.put(ctrlCKap.key, ctrlCKap.action);
+
+ comp.addCaretListener(this);
+
+ }
+
+
+ public void mouseClicked(MouseEvent e) {
+ if (e.getClickCount() == 2 && e.getButton() == 1) {
+ insertSelectedCompletion();
+ }
+ }
+
+
+ public void mouseEntered(MouseEvent e) {
+ }
+
+
+ public void mouseExited(MouseEvent e) {
+ }
+
+
+ public void mousePressed(MouseEvent e) {
+ refreshInstallComp();
+ }
+
+
+ public void mouseReleased(MouseEvent e) {
+ }
+
+
+ /**
+ * Positions the description window relative to the completion choices
+ * window. We assume there is room on one side of the other for this
+ * entire window to fit.
+ */
+ private void positionDescWindow() {
+
+ boolean showDescWindow = descWindow != null && ac.isShowDescWindow();
+ if (!showDescWindow) {
+ return;
+ }
+
+ // Don't use getLocationOnScreen() as this throws an exception if
+ // window isn't visible yet, but getLocation() doesn't, and is in
+ // screen coordinates!
+ Point p = getLocation();
+ Rectangle screenBounds = Util.getScreenBoundsForPoint(p.x, p.y);
+ //Dimension screenSize = getToolkit().getScreenSize();
+ //int totalH = Math.max(getHeight(), descWindow.getHeight());
+
+ // Try to position to the right first (LTR)
+ int x;
+ if (ac.getTextComponentOrientation().isLeftToRight()) {
+ x = getX() + getWidth() + DIS;
+ if (x + descWindow.getWidth() > screenBounds.x + screenBounds.width) { // doesn't fit
+ x = getX() - DIS - descWindow.getWidth();
+ }
+ } else { // RTL
+ x = getX() - DIS - descWindow.getWidth();
+ if (x < screenBounds.x) { // Doesn't fit
+ x = getX() + getWidth() + DIS;
+ }
+ }
+
+ int y = getY();
+ if (aboveCaret) {
+ y = y + getHeight() - descWindow.getHeight();
+ }
+
+ if (x != descWindow.getX() || y != descWindow.getY()) {
+ descWindow.setLocation(x, y);
+ }
+
+ }
+
+
+ /**
+ * "Puts back" the original key/Action pair for a keystroke. This is used
+ * when this popup is hidden.
+ *
+ * @param im The input map.
+ * @param am The action map.
+ * @param key The keystroke whose key/Action pair to change.
+ * @param kap The (original) key/Action pair.
+ * @see #replaceAction(InputMap, ActionMap, int, KeyActionPair, KeyActionPair)
+ */
+ private void putBackAction(InputMap im, ActionMap am, int key,
+ KeyActionPair kap) {
+ KeyStroke ks = KeyStroke.getKeyStroke(key, 0);
+ am.put(im.get(ks), kap.action); // Original action for the "new" key
+ im.put(ks, kap.key); // Original key for the keystroke.
+ }
+
+
+ /**
+ * Replaces a key/Action pair in an InputMap and ActionMap with a new
+ * pair.
+ *
+ * @param im The input map.
+ * @param am The action map.
+ * @param key The keystroke whose information to replace.
+ * @param kap The new key/Action pair for key
.
+ * @param old A buffer in which to place the old key/Action pair.
+ * @see #putBackAction(InputMap, ActionMap, int, KeyActionPair)
+ */
+ private void replaceAction(InputMap im, ActionMap am, int key,
+ KeyActionPair kap, KeyActionPair old) {
+ KeyStroke ks = KeyStroke.getKeyStroke(key, 0);
+ old.key = im.get(ks);
+ im.put(ks, kap.key);
+ old.action = am.get(kap.key);
+ am.put(kap.key, kap.action);
+ }
+
+
+ /**
+ * Selects the first item in the completion list.
+ *
+ * @see #selectLastItem()
+ */
+ private void selectFirstItem() {
+ if (model.getSize() > 0) {
+ list.setSelectedIndex(0);
+ list.ensureIndexIsVisible(0);
+ }
+ }
+
+
+ /**
+ * Selects the last item in the completion list.
+ *
+ * @see #selectFirstItem()
+ */
+ private void selectLastItem() {
+ int index = model.getSize() - 1;
+ if (index > -1) {
+ list.setSelectedIndex(index);
+ list.ensureIndexIsVisible(index);
+ }
+ }
+
+
+ /**
+ * Selects the next item in the completion list.
+ *
+ * @see #selectPreviousItem()
+ */
+ private void selectNextItem() {
+ int index = list.getSelectedIndex();
+ if (index > -1) {
+ index = (index + 1) % model.getSize();
+ list.setSelectedIndex(index);
+ list.ensureIndexIsVisible(index);
+ }
+ }
+
+
+ /**
+ * Selects the completion item one "page down" from the currently selected
+ * one.
+ *
+ * @see #selectPageUpItem()
+ */
+ private void selectPageDownItem() {
+ int visibleRowCount = list.getVisibleRowCount();
+ int i = Math.min(list.getModel().getSize() - 1,
+ list.getSelectedIndex() + visibleRowCount);
+ list.setSelectedIndex(i);
+ list.ensureIndexIsVisible(i);
+ }
+
+
+ /**
+ * Selects the completion item one "page up" from the currently selected
+ * one.
+ *
+ * @see #selectPageDownItem()
+ */
+ private void selectPageUpItem() {
+ int visibleRowCount = list.getVisibleRowCount();
+ int i = Math.max(0, list.getSelectedIndex() - visibleRowCount);
+ list.setSelectedIndex(i);
+ list.ensureIndexIsVisible(i);
+ }
+
+
+ /**
+ * Selects the previous item in the completion list.
+ *
+ * @see #selectNextItem()
+ */
+ private void selectPreviousItem() {
+ int index = list.getSelectedIndex();
+ switch (index) {
+ case 0:
+ index = list.getModel().getSize() - 1;
+ break;
+ case -1: // Check for an empty list (would be an error)
+ index = list.getModel().getSize() - 1;
+ if (index == -1) {
+ return;
+ }
+ break;
+ default:
+ index = index - 1;
+ break;
+ }
+ list.setSelectedIndex(index);
+ list.ensureIndexIsVisible(index);
+ }
+
+
+ /**
+ * Sets the completions to display in the choices list. The first
+ * completion is selected.
+ *
+ * @param completions The completions to display.
+ */
+ public void setCompletions(List completions) {
+ model.setContents(completions);
+ selectFirstItem();
+ }
+
+
+ /**
+ * Sets the size of the description window.
+ *
+ * @param size The new size. This cannot be null
.
+ */
+ public void setDescriptionWindowSize(Dimension size) {
+ if (descWindow != null) {
+ descWindow.setSize(size);
+ } else {
+ preferredDescWindowSize = size;
+ }
+ }
+
+
+ /**
+ * Sets the default list cell renderer to use when a completion provider
+ * does not supply its own.
+ *
+ * @param renderer The renderer to use. If this is null
,
+ * a default renderer is used.
+ * @see #getListCellRenderer()
+ */
+ public void setListCellRenderer(ListCellRenderer renderer) {
+ DelegatingCellRenderer dcr = (DelegatingCellRenderer) list.
+ getCellRenderer();
+ dcr.setFallbackCellRenderer(renderer);
+ }
+
+
+ /**
+ * Sets the location of this window to be "good" relative to the specified
+ * rectangle. That rectangle should be the location of the text
+ * component's caret, in screen coordinates.
+ *
+ * @param r The text component's caret position, in screen coordinates.
+ */
+ public void setLocationRelativeTo(Rectangle r) {
+
+ // Multi-monitor support - make sure the completion window (and
+ // description window, if applicable) both fit in the same window in
+ // a multi-monitor environment. To do this, we decide which monitor
+ // the rectangle "r" is in, and use that one (just pick top-left corner
+ // as the defining point).
+ Rectangle screenBounds = Util.getScreenBoundsForPoint(r.x, r.y);
+ //Dimension screenSize = getToolkit().getScreenSize();
+
+ boolean showDescWindow = descWindow != null && ac.isShowDescWindow();
+ int totalH = getHeight();
+ if (showDescWindow) {
+ totalH = Math.max(totalH, descWindow.getHeight());
+ }
+
+ // Try putting our stuff "below" the caret first. We assume that the
+ // entire height of our stuff fits on the screen one way or the other.
+ aboveCaret = false;
+ int y = r.y + r.height + VERTICAL_SPACE;
+ if (y + totalH > screenBounds.height) {
+ y = r.y - VERTICAL_SPACE - getHeight();
+ aboveCaret = true;
+ }
+
+ // Get x-coordinate of completions. Try to align left edge with the
+ // caret first.
+ int x = r.x;
+ if (!ac.getTextComponentOrientation().isLeftToRight()) {
+ x -= getWidth(); // RTL => align right edge
+ }
+ if (x < screenBounds.x) {
+ x = screenBounds.x;
+ } else if (x + getWidth() > screenBounds.x + screenBounds.width) { // completions don't fit
+ x = screenBounds.x + screenBounds.width - getWidth();
+ }
+
+ setLocation(x, y);
+
+ // Position the description window, if necessary.
+ if (showDescWindow) {
+ positionDescWindow();
+ }
+
+ }
+
+
+ /**
+ * Toggles the visibility of this popup window.
+ *
+ * @param visible Whether this window should be visible.
+ */
+ @Override
+ public void setVisible(boolean visible) {
+
+ if (visible != isVisible()) {
+
+ if (visible) {
+ installKeyBindings();
+ lastLine = ac.getLineOfCaret();
+ selectFirstItem();
+ if (descWindow == null && ac.isShowDescWindow()) {
+ descWindow = createDescriptionWindow();
+ positionDescWindow();
+ }
+ // descWindow needs a kick-start the first time it's displayed.
+ // Also, the newly-selected item in the choices list is
+ // probably different from the previous one anyway.
+ if (descWindow != null) {
+ Completion c = (Completion) list.getSelectedValue();
+ if (c != null) {
+ descWindow.setDescriptionFor(c);
+ }
+ }
+ } else {
+ uninstallKeyBindings();
+ }
+
+ super.setVisible(visible);
+
+ // Some languages, such as Java, can use quite a lot of memory
+ // when displaying hundreds of completion choices. We pro-actively
+ // clear our list model here to make them available for GC.
+ // Otherwise, they stick around, and consider the following: a
+ // user starts code-completion for Java 5 SDK classes, then hides
+ // the dialog, then changes the "class path" to use a Java 6 SDK
+ // instead. On pressing Ctrl+space, a new array of Completions is
+ // created. If this window holds on to the previous Completions,
+ // you're getting roughly 2x the necessary Completions in memory
+ // until the Completions are actually passed to this window.
+ if (!visible) { // Do after super.setVisible(false)
+ lastSelection = (Completion) list.getSelectedValue();
+ model.clear();
+ }
+
+ // Must set descWindow's visibility one way or the other each time,
+ // 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.isShowDescWindow());
+ }
+
+ }
+
+ }
+
+
+ /**
+ * Stops intercepting certain keystrokes from the text component.
+ *
+ * @see #installKeyBindings()
+ */
+ private void uninstallKeyBindings() {
+
+ if (AutoCompletion.isDebug()) {
+ FineLoggerFactory.getLogger().debug("PopupWindow: Removing keybindings");
+ }
+
+ JTextComponent comp = ac.getTextComponent();
+ InputMap im = comp.getInputMap();
+ ActionMap am = comp.getActionMap();
+
+ putBackAction(im, am, KeyEvent.VK_ESCAPE, oldEscape);
+ putBackAction(im, am, KeyEvent.VK_UP, oldUp);
+ putBackAction(im, am, KeyEvent.VK_DOWN, oldDown);
+ putBackAction(im, am, KeyEvent.VK_LEFT, oldLeft);
+ putBackAction(im, am, KeyEvent.VK_RIGHT, oldRight);
+ putBackAction(im, am, KeyEvent.VK_ENTER, oldEnter);
+ putBackAction(im, am, KeyEvent.VK_TAB, oldTab);
+ putBackAction(im, am, KeyEvent.VK_HOME, oldHome);
+ putBackAction(im, am, KeyEvent.VK_END, oldEnd);
+ putBackAction(im, am, KeyEvent.VK_PAGE_UP, oldPageUp);
+ putBackAction(im, am, KeyEvent.VK_PAGE_DOWN, oldPageDown);
+
+ // Ctrl+C
+ KeyStroke ks = getCopyKeyStroke();
+ am.put(im.get(ks), oldCtrlC.action); // Original action
+ im.put(ks, oldCtrlC.key); // Original key
+
+ comp.removeCaretListener(this);
+
+ }
+
+
+ /**
+ * Updates the LookAndFeel of this window and the description
+ * window.
+ */
+ public void updateUI() {
+ SwingUtilities.updateComponentTreeUI(this);
+ if (descWindow != null) {
+ descWindow.updateUI();
+ }
+ }
+
+
+ /**
+ * Called when a new item is selected in the popup list.
+ *
+ * @param e The event.
+ */
+ public void valueChanged(ListSelectionEvent e) {
+ if (!e.getValueIsAdjusting()) {
+ Object value = list.getSelectedValue();
+ if (value != null && descWindow != null) {
+ descWindow.setDescriptionFor((Completion) value);
+ positionDescWindow();
+ }
+ }
+ }
+
+
+ class CopyAction extends AbstractAction {
+
+ public void actionPerformed(ActionEvent e) {
+ boolean doNormalCopy = false;
+ if (descWindow != null && descWindow.isVisible()) {
+ doNormalCopy = !descWindow.copy();
+ }
+ if (doNormalCopy) {
+ ac.getTextComponent().copy();
+ }
+ }
+
+ }
+
+
+ class DownAction extends AbstractAction {
+
+ public void actionPerformed(ActionEvent e) {
+ if (isVisible()) {
+ selectNextItem();
+ refreshInstallComp();
+ }
+ }
+
+ }
+
+
+ class EndAction extends AbstractAction {
+
+ public void actionPerformed(ActionEvent e) {
+ if (isVisible()) {
+ selectLastItem();
+ }
+ }
+
+ }
+
+
+ class EnterAction extends AbstractAction {
+
+ public void actionPerformed(ActionEvent e) {
+ if (isVisible()) {
+ insertSelectedCompletion();
+ refreshInstallComp();
+ }
+ }
+
+ }
+
+
+ class EscapeAction extends AbstractAction {
+
+ public void actionPerformed(ActionEvent e) {
+ if (isVisible()) {
+ setVisible(false);
+ }
+ }
+
+ }
+
+
+ class HomeAction extends AbstractAction {
+
+ public void actionPerformed(ActionEvent e) {
+ if (isVisible()) {
+ selectFirstItem();
+ }
+ }
+
+ }
+
+
+ /**
+ * A mapping from a key (an Object) to an Action.
+ */
+ private static class KeyActionPair {
+
+ public Object key;
+ public Action action;
+
+ public KeyActionPair() {
+ }
+
+ public KeyActionPair(Object key, Action a) {
+ this.key = key;
+ this.action = a;
+ }
+
+ }
+
+
+ class LeftAction extends AbstractAction {
+
+ public void actionPerformed(ActionEvent e) {
+ if (isVisible()) {
+ JTextComponent comp = ac.getTextComponent();
+ Caret c = comp.getCaret();
+ int dot = c.getDot();
+ if (dot > 0) {
+ c.setDot(--dot);
+ // Ensure moving left hasn't moved us up a line, thus
+ // hiding the popup window.
+ if (comp.isVisible()) {
+ if (lastLine != -1) {
+ doAutocomplete();
+ }
+ }
+ }
+ }
+ }
+
+ }
+
+
+ class PageDownAction extends AbstractAction {
+
+ public void actionPerformed(ActionEvent e) {
+ if (isVisible()) {
+ selectPageDownItem();
+ }
+ }
+
+ }
+
+
+ class PageUpAction extends AbstractAction {
+
+ public void actionPerformed(ActionEvent e) {
+ if (isVisible()) {
+ selectPageUpItem();
+ }
+ }
+
+ }
+
+
+ /**
+ * The actual list of completion choices in this popup window.
+ */
+ private class PopupList extends JList {
+
+ public PopupList(CompletionListModel model) {
+ super(model);
+ }
+
+ @Override
+ public void setUI(ListUI ui) {
+ if (Util.getUseSubstanceRenderers() &&
+ SUBSTANCE_LIST_UI.equals(ui.getClass().getName())) {
+ // Substance requires its special ListUI be installed for
+ // its renderers to actually render (!), but long completion
+ // lists (e.g. PHPCompletionProvider in RSTALanguageSupport)
+ // will simply populate too slowly on initial display (when
+ // calculating preferred size of all items), so in this case
+ // we give a prototype cell value.
+ CompletionProvider p = ac.getCompletionProvider();
+ BasicCompletion bc = new BasicCompletion(p, "Hello world");
+ setPrototypeCellValue(bc);
+ } else {
+ // Our custom UI that is faster for long HTML completion lists.
+ ui = new FastListUI();
+ setPrototypeCellValue(null);
+ }
+ super.setUI(ui);
+ }
+
+ }
+
+
+ class RightAction extends AbstractAction {
+
+ public void actionPerformed(ActionEvent e) {
+ if (isVisible()) {
+ JTextComponent comp = ac.getTextComponent();
+ Caret c = comp.getCaret();
+ int dot = c.getDot();
+ if (dot < comp.getDocument().getLength()) {
+ c.setDot(++dot);
+ // Ensure moving right hasn't moved us up a line, thus
+ // hiding the popup window.
+ if (comp.isVisible()) {
+ if (lastLine != -1) {
+ doAutocomplete();
+ }
+ }
+ }
+ }
+ }
+
+ }
+
+
+ class UpAction extends AbstractAction {
+
+ public void actionPerformed(ActionEvent e) {
+ if (isVisible()) {
+ selectPreviousItem();
+ refreshInstallComp();
+ }
+ }
+
+ }
+
+}
diff --git a/designer-base/src/main/java/com/fr/design/gui/autocomplete/AutoCompletionWithExtraRefresh.java b/designer-base/src/main/java/com/fr/design/gui/autocomplete/AutoCompletionWithExtraRefresh.java
new file mode 100644
index 000000000..4def5370d
--- /dev/null
+++ b/designer-base/src/main/java/com/fr/design/gui/autocomplete/AutoCompletionWithExtraRefresh.java
@@ -0,0 +1,1260 @@
+package com.fr.design.gui.autocomplete;
+
+import com.fr.design.formula.FormulaPane;
+import java.awt.ComponentOrientation;
+import java.awt.Dimension;
+import java.awt.Point;
+import java.awt.Rectangle;
+import java.awt.Window;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.ComponentAdapter;
+import java.awt.event.ComponentEvent;
+import java.awt.event.FocusAdapter;
+import java.awt.event.FocusEvent;
+import java.awt.event.HierarchyEvent;
+import java.awt.event.HierarchyListener;
+import java.awt.event.KeyEvent;
+import java.awt.event.WindowEvent;
+import java.awt.event.WindowFocusListener;
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+import java.util.List;
+import javax.swing.AbstractAction;
+import javax.swing.Action;
+import javax.swing.ActionMap;
+import javax.swing.InputMap;
+import javax.swing.JComponent;
+import javax.swing.KeyStroke;
+import javax.swing.ListCellRenderer;
+import javax.swing.SwingUtilities;
+import javax.swing.Timer;
+import javax.swing.UIManager;
+import javax.swing.event.CaretEvent;
+import javax.swing.event.CaretListener;
+import javax.swing.event.DocumentEvent;
+import javax.swing.event.DocumentListener;
+import javax.swing.text.BadLocationException;
+import javax.swing.text.Caret;
+import javax.swing.text.Document;
+import javax.swing.text.Element;
+import javax.swing.text.JTextComponent;
+
+
+import static com.fr.design.gui.syntax.ui.rtextarea.RTADefaultInputMap.DEFAULT_MODIFIER;
+
+public abstract class AutoCompletionWithExtraRefresh extends AutoCompletion{
+ private AutoCompleteExtraRefreshComponent area;
+
+ /**
+ * The text component we're providing completion for.
+ */
+ private JTextComponent textComponent;
+
+ /**
+ * The parent window of {@link #textComponent}.
+ */
+ private Window parentWindow;
+
+ /**
+ * The popup window containing completion choices.
+ */
+ private AutoCompleteWithExtraRefreshPopupWindow popupWindow;
+
+ /**
+ * The preferred size of the completion choices window. This field exists
+ * because the user will likely set the preferred size of the window
+ * before it is actually created.
+ */
+ private Dimension preferredChoicesWindowSize;
+
+ /**
+ * The preferred size of the optional description window. This field
+ * only exists because the user may (and usually will) set the size of
+ * the description window before it exists (it must be parented to a
+ * Window).
+ */
+ private Dimension preferredDescWindowSize;
+
+ /**
+ * Manages any parameterized completions that are inserted.
+ */
+ private ParameterizedCompletionContext pcc;
+
+ /**
+ * Provides the completion options relevant to the current caret position.
+ */
+ private CompletionProvider provider;
+
+ /**
+ * The renderer to use for the completion choices. If this is
+ * null
, then a default renderer is used.
+ */
+ private ListCellRenderer renderer;
+
+ /**
+ * The handler to use when an external URL is clicked in the help
+ * documentation.
+ */
+ private ExternalURLHandler externalURLHandler;
+
+ /**
+ * An optional redirector that converts URL's to some other location before
+ * being handed over to externalURLHandler
.
+ */
+ private static LinkRedirector linkRedirector;
+
+ /**
+ * Whether the description window should be displayed along with the
+ * completion choice window.
+ */
+ private boolean showDescWindow;
+
+ /**
+ * Whether auto-complete is enabled.
+ */
+ private boolean autoCompleteEnabled;
+
+ /**
+ * Whether the auto-activation of auto-complete (after a delay, after the
+ * user types an appropriate character) is enabled.
+ */
+ private boolean autoActivationEnabled;
+
+ /**
+ * Whether or not, when there is only a single auto-complete option
+ * that matches the text at the current text position, that text should
+ * be auto-inserted, instead of the completion window displaying.
+ */
+ private boolean autoCompleteSingleChoices;
+
+ /**
+ * Whether parameter assistance is enabled.
+ */
+ private boolean parameterAssistanceEnabled;
+
+ /**
+ * A renderer used for {@link Completion}s in the optional parameter
+ * choices popup window (displayed when a {@link ParameterizedCompletion}
+ * is code-completed). If this isn't set, a default renderer is used.
+ */
+ private ListCellRenderer paramChoicesRenderer;
+
+ /**
+ * The keystroke that triggers the completion window.
+ */
+ private KeyStroke trigger;
+
+ /**
+ * The previous key in the text component's InputMap
for the
+ * trigger key.
+ */
+ private Object oldTriggerKey;
+
+ /**
+ * The action previously assigned to {@link #trigger}, so we can reset it
+ * if the user disables auto-completion.
+ */
+ private Action oldTriggerAction;
+
+ /**
+ * The previous key in the text component's InputMap
for the
+ * parameter completion trigger key.
+ */
+ private Object oldParenKey;
+
+ /**
+ * The action previously assigned to the parameter completion key, so we
+ * can reset it when we uninstall.
+ */
+ private Action oldParenAction;
+
+ /**
+ * Listens for events in the parent window that affect the visibility of
+ * the popup windows.
+ */
+ private ParentWindowListener parentWindowListener;
+
+ /**
+ * Listens for events from the text component that affect the visibility
+ * of the popup windows.
+ */
+ private TextComponentListener textComponentListener;
+
+ /**
+ * Listens for events in the text component that cause the popup windows
+ * to automatically activate.
+ */
+ private AutoActivationListener autoActivationListener;
+
+ /**
+ * Listens for LAF changes so the auto-complete windows automatically
+ * update themselves accordingly.
+ */
+ private LookAndFeelChangeListener lafListener;
+
+ /**
+ * The key used in the input map for the AutoComplete action.
+ */
+ private static final String PARAM_TRIGGER_KEY = "AutoComplete";
+
+ /**
+ * Key used in the input map for the parameter completion action.
+ */
+ private static final String PARAM_COMPLETE_KEY = "AutoCompletion.FunctionStart";
+
+ /**
+ * Stores how to render auto-completion-specific highlights in text
+ * components.
+ */
+ private static final AutoCompletionStyleContext STYLE_CONTEXT =
+ new AutoCompletionStyleContext();
+
+ /**
+ * Whether debug messages should be printed to stdout as AutoCompletion
+ * runs.
+ */
+ private static final boolean DEBUG = initDebug();
+
+ Window getParentWindow(){
+ return parentWindow;
+ }
+
+ ListCellRenderer getCellRender(){
+ return renderer;
+ }
+
+ Dimension getPreferredChoicesWindowSize(){
+ return preferredChoicesWindowSize;
+ }
+
+ Dimension getPreferredDescWindowSize(){
+ return preferredDescWindowSize;
+ }
+ /**
+ * Constructor.
+ *
+ * @param provider The completion provider. This cannot be
+ * null
.
+ */
+ public AutoCompletionWithExtraRefresh(CompletionProvider provider) {
+ super(provider);
+ setChoicesWindowSize(350, 200);
+ setDescriptionWindowSize(350, 250);
+
+ setCompletionProvider(provider);
+ setTriggerKey(getDefaultTriggerKey());
+ setAutoCompleteEnabled(true);
+ setAutoCompleteSingleChoices(true);
+ setAutoActivationEnabled(false);
+ setShowDescWindow(false);
+ parentWindowListener = new ParentWindowListener();
+ textComponentListener = new TextComponentListener();
+ autoActivationListener = new AutoActivationListener();
+ lafListener = new LookAndFeelChangeListener();
+ }
+
+
+ /**
+ * Displays the popup window. Hosting applications can call this method
+ * to programmatically begin an auto-completion operation.
+ */
+ public void doCompletion() {
+ refreshPopupWindow();
+ }
+
+
+ /**
+ * Returns the delay between when the user types a character and when the
+ * code completion popup should automatically appear (if applicable).
+ *
+ * @return The delay, in milliseconds.
+ * @see #setAutoActivationDelay(int)
+ */
+ public int getAutoActivationDelay() {
+ return autoActivationListener.timer.getDelay();
+ }
+
+
+ /**
+ * Returns whether, if a single auto-complete choice is available, it
+ * should be automatically inserted, without displaying the popup menu.
+ *
+ * @return Whether to auto-complete single choices.
+ * @see #setAutoCompleteSingleChoices(boolean)
+ */
+ public boolean isAutoCompleteSingleChoices() {
+ return autoCompleteSingleChoices;
+ }
+
+
+ /**
+ * Returns the completion provider.
+ *
+ * @return The completion provider.
+ */
+ public CompletionProvider getCompletionProvider() {
+ return provider;
+ }
+
+
+ /**
+ * Returns whether debug is enabled for AutoCompletion.
+ *
+ * @return Whether debug is enabled.
+ */
+ static boolean isDebug() {
+ return DEBUG;
+ }
+
+
+ /**
+ * Returns the default auto-complete "trigger key" for this OS. For
+ * Windows, for example, it is Ctrl+Space.
+ *
+ * @return The default auto-complete trigger key.
+ */
+ public static KeyStroke getDefaultTriggerKey() {
+ // Default to CTRL, even on Mac, since Ctrl+Space activates Spotlight
+ return KeyStroke.getKeyStroke(KeyEvent.VK_SPACE, DEFAULT_MODIFIER);
+ }
+
+
+ /**
+ * Returns the handler to use when an external URL is clicked in the
+ * description window.
+ *
+ * @return The handler.
+ * @see #setExternalURLHandler(ExternalURLHandler)
+ * @see #getLinkRedirector()
+ */
+ public ExternalURLHandler getExternalURLHandler() {
+ return externalURLHandler;
+ }
+
+
+ int getLineOfCaret() {
+ Document doc = textComponent.getDocument();
+ Element root = doc.getDefaultRootElement();
+ return root.getElementIndex(textComponent.getCaretPosition());
+ }
+
+
+ /**
+ * Returns the link redirector, if any.
+ *
+ * @return The link redirector, or null
if none.
+ * @see #setLinkRedirector(LinkRedirector)
+ */
+ public static LinkRedirector getLinkRedirector() {
+ return linkRedirector;
+ }
+
+
+ /**
+ * Returns the default list cell renderer used when a completion provider
+ * does not supply its own.
+ *
+ * @return The default list cell renderer.
+ * @see #setListCellRenderer(ListCellRenderer)
+ */
+ public ListCellRenderer getListCellRenderer() {
+ return renderer;
+ }
+
+
+ /**
+ * Returns the renderer to use for {@link Completion}s in the optional
+ * parameter choices popup window (displayed when a
+ * {@link ParameterizedCompletion} is code-completed). If this returns
+ * null
, a default renderer is used.
+ *
+ * @return The renderer to use.
+ * @see #setParamChoicesRenderer(ListCellRenderer)
+ * @see #isParameterAssistanceEnabled()
+ */
+ public ListCellRenderer getParamChoicesRenderer() {
+ return paramChoicesRenderer;
+ }
+
+
+ /**
+ * Returns the text to replace with in the document. This is a
+ * "last-chance" hook for subclasses to make special modifications to the
+ * completion text inserted. The default implementation simply returns
+ * c.getReplacementText(). You usually will not need to modify
+ * this method.
+ *
+ * @param c The completion being inserted.
+ * @param doc The document being modified.
+ * @param start The start of the text being replaced.
+ * @param len The length of the text being replaced.
+ * @return The text to replace with.
+ */
+ protected String getReplacementText(Completion c, Document doc, int start,
+ int len) {
+ return c.getReplacementText();
+ }
+
+
+ /**
+ * Returns whether the "description window" should be shown alongside
+ * the completion window.
+ *
+ * @return Whether the description window should be shown.
+ * @see #setShowDescWindow(boolean)
+ */
+ public boolean isShowDescWindow() {
+ return showDescWindow;
+ }
+
+
+ /**
+ * Returns the style context describing how auto-completion related
+ * highlights in the editor are rendered.
+ *
+ * @return The style context.
+ */
+ public static AutoCompletionStyleContext getStyleContext() {
+ return STYLE_CONTEXT;
+ }
+
+
+ /**
+ * Returns the text component for which auto-completion is enabled.
+ *
+ * @return The text component, or null
if this
+ * {@link AutoCompletion} is not installed on any text component.
+ * @see #install(JTextComponent)
+ */
+ public JTextComponent getTextComponent() {
+ return textComponent;
+ }
+
+
+ /**
+ * Returns the orientation of the text component we're installed to.
+ *
+ * @return The orientation of the text component, or null
if
+ * we are not installed on one.
+ */
+ ComponentOrientation getTextComponentOrientation() {
+ return textComponent == null ? null :
+ textComponent.getComponentOrientation();
+ }
+
+
+ /**
+ * Returns the "trigger key" used for auto-complete.
+ *
+ * @return The trigger key.
+ * @see #setTriggerKey(KeyStroke)
+ */
+ public KeyStroke getTriggerKey() {
+ return trigger;
+ }
+
+
+ /**
+ * Hides any child windows being displayed by the auto-completion system.
+ *
+ * @return Whether any windows were visible.
+ */
+ public boolean hideChildWindows() {
+ //return hidePopupWindow() || hideToolTipWindow();
+ boolean res = hidePopupWindow();
+ res |= hideParameterCompletionPopups();
+ return res;
+ }
+
+
+ /**
+ * Hides and disposes of any parameter completion-related popups.
+ *
+ * @return Whether any such windows were visible (and thus hidden).
+ */
+ private boolean hideParameterCompletionPopups() {
+ if (pcc != null) {
+ pcc.deactivate();
+ pcc = null;
+ return true;
+ }
+ return false;
+ }
+
+
+ /**
+ * Hides the popup window, if it is visible.
+ *
+ * @return Whether the popup window was visible.
+ */
+ protected boolean hidePopupWindow() {
+ if (popupWindow != null) {
+ if (popupWindow.isVisible()) {
+ popupWindow.setVisible(false);
+ return true;
+ }
+ }
+ return false;
+ }
+
+
+ /**
+ * Determines whether debug should be enabled for the AutoCompletion
+ * library. This method checks a system property, but takes care of
+ * {@link SecurityException}s in case we're in an applet or WebStart.
+ *
+ * @return Whether debug should be enabled.
+ */
+ private static boolean initDebug() {
+ boolean debug = false;
+ try {
+ debug = Boolean.getBoolean("AutoCompletion.debug");
+ } catch (SecurityException se) { // We're in an applet or WebStart.
+ debug = false;
+ }
+ return debug;
+ }
+
+
+ /**
+ * Installs this auto-completion on a text component. If this
+ * {@link AutoCompletion} is already installed on another text component,
+ * it is uninstalled first.
+ *
+ * @param c The text component.
+ * @see #uninstall()
+ */
+ public void install(JTextComponent c) {
+
+ if (textComponent != null) {
+ uninstall();
+ }
+
+ this.textComponent = c;
+ installTriggerKey(getTriggerKey());
+
+ // Install the function completion key, if there is one.
+ // NOTE: We cannot do this if the start char is ' ' (e.g. just a space
+ // between the function name and parameters) because it overrides
+ // RSTA's special space action. It seems KeyStorke.getKeyStroke(' ')
+ // hoses ctrl+space, shift+space, etc., even though I think it
+ // shouldn't...
+ char start = provider.getParameterListStart();
+ if (start != 0 && start != ' ') {
+ InputMap im = c.getInputMap();
+ ActionMap am = c.getActionMap();
+ KeyStroke ks = KeyStroke.getKeyStroke(start);
+ oldParenKey = im.get(ks);
+ im.put(ks, PARAM_COMPLETE_KEY);
+ oldParenAction = am.get(PARAM_COMPLETE_KEY);
+ am.put(PARAM_COMPLETE_KEY,
+ new ParameterizedCompletionStartAction(start));
+ }
+
+ textComponentListener.addTo(this.textComponent);
+ // In case textComponent is already in a window...
+ textComponentListener.hierarchyChanged(null);
+
+ if (isAutoActivationEnabled()) {
+ autoActivationListener.addTo(this.textComponent);
+ }
+
+ UIManager.addPropertyChangeListener(lafListener);
+ updateUI(); // In case there have been changes since we uninstalled
+
+ }
+
+
+ /**
+ * Installs a "trigger key" action onto the current text component.
+ *
+ * @param ks The keystroke that should trigger the action.
+ */
+ private void installTriggerKey(KeyStroke ks) {
+ InputMap im = textComponent.getInputMap();
+ oldTriggerKey = im.get(ks);
+ im.put(ks, PARAM_TRIGGER_KEY);
+ ActionMap am = textComponent.getActionMap();
+ oldTriggerAction = am.get(PARAM_TRIGGER_KEY);
+ am.put(PARAM_TRIGGER_KEY, new AutoCompleteAction());
+ }
+
+
+ /**
+ * Returns whether auto-activation is enabled (that is, whether the
+ * completion popup will automatically appear after a delay when the user
+ * types an appropriate character). Note that this parameter will be
+ * ignored if auto-completion is disabled.
+ *
+ * @return Whether auto-activation is enabled.
+ * @see #setAutoActivationEnabled(boolean)
+ * @see #getAutoActivationDelay()
+ * @see #isAutoCompleteEnabled()
+ */
+ public boolean isAutoActivationEnabled() {
+ return autoActivationEnabled;
+ }
+
+
+ /**
+ * Returns whether auto-completion is enabled.
+ *
+ * @return Whether auto-completion is enabled.
+ * @see #setAutoCompleteEnabled(boolean)
+ */
+ public boolean isAutoCompleteEnabled() {
+ return autoCompleteEnabled;
+ }
+
+
+ /**
+ * Returns whether parameter assistance is enabled.
+ *
+ * @return Whether parameter assistance is enabled.
+ * @see #setParameterAssistanceEnabled(boolean)
+ */
+ public boolean isParameterAssistanceEnabled() {
+ return parameterAssistanceEnabled;
+ }
+
+
+ /**
+ * Returns whether the completion popup window is visible.
+ *
+ * @return Whether the completion popup window is visible.
+ */
+ public boolean isPopupVisible() {
+ return popupWindow != null && popupWindow.isVisible();
+ }
+
+ private boolean needSetPopupWindow(int count, int textLen) {
+ return (count == 1 && (isPopupVisible() || textLen == 0))
+ || (count == 1 && !isAutoCompleteSingleChoices())
+ || count > 1;
+ }
+
+ protected abstract AutoCompleteWithExtraRefreshPopupWindow createAutoCompletePopupWindow();
+
+ /**
+ * Sets the delay between when the user types a character and when the
+ * code completion popup should automatically appear (if applicable).
+ *
+ * @param ms The delay, in milliseconds. This should be greater than zero.
+ * @see #getAutoActivationDelay()
+ */
+ public void setAutoActivationDelay(int ms) {
+ ms = Math.max(0, ms);
+ autoActivationListener.timer.stop();
+ autoActivationListener.timer.setInitialDelay(ms);
+ }
+
+
+ /**
+ * Toggles whether auto-activation is enabled. Note that auto-activation
+ * also depends on auto-completion itself being enabled.
+ *
+ * @param enabled Whether auto-activation is enabled.
+ * @see #isAutoActivationEnabled()
+ * @see #setAutoActivationDelay(int)
+ */
+ public void setAutoActivationEnabled(boolean enabled) {
+ if (enabled != autoActivationEnabled) {
+ autoActivationEnabled = enabled;
+ if (textComponent != null) {
+ if (autoActivationEnabled) {
+ autoActivationListener.addTo(textComponent);
+ } else {
+ autoActivationListener.removeFrom(textComponent);
+ }
+ }
+ }
+ }
+
+
+ /**
+ * Sets whether auto-completion is enabled.
+ *
+ * @param enabled Whether auto-completion is enabled.
+ * @see #isAutoCompleteEnabled()
+ */
+ public void setAutoCompleteEnabled(boolean enabled) {
+ if (enabled != autoCompleteEnabled) {
+ autoCompleteEnabled = enabled;
+ hidePopupWindow();
+ }
+ }
+
+
+ /**
+ * Sets whether, if a single auto-complete choice is available, it should
+ * be automatically inserted, without displaying the popup menu.
+ *
+ * @param autoComplete Whether to auto-complete single choices.
+ * @see #isAutoCompleteSingleChoices()
+ */
+ public void setAutoCompleteSingleChoices(boolean autoComplete) {
+ autoCompleteSingleChoices = autoComplete;
+ }
+
+
+ /**
+ * Sets the completion provider being used.
+ *
+ * @param provider The new completion provider. This cannot be
+ * null
.
+ * @throws IllegalArgumentException If provider
is
+ * null
.
+ */
+ public void setCompletionProvider(CompletionProvider provider) {
+ if (provider == null) {
+ throw new IllegalArgumentException("provider cannot be null");
+ }
+ this.provider = provider;
+ hidePopupWindow(); // In case new choices should be displayed.
+ }
+
+
+ /**
+ * Sets the size of the completion choices window.
+ *
+ * @param w The new width.
+ * @param h The new height.
+ * @see #setDescriptionWindowSize(int, int)
+ */
+ public void setChoicesWindowSize(int w, int h) {
+ preferredChoicesWindowSize = new Dimension(w, h);
+ if (popupWindow != null) {
+ popupWindow.setSize(preferredChoicesWindowSize);
+ }
+ }
+
+
+ /**
+ * Sets the size of the description window.
+ *
+ * @param w The new width.
+ * @param h The new height.
+ * @see #setChoicesWindowSize(int, int)
+ */
+ public void setDescriptionWindowSize(int w, int h) {
+ preferredDescWindowSize = new Dimension(w, h);
+ if (popupWindow != null) {
+ popupWindow.setDescriptionWindowSize(preferredDescWindowSize);
+ }
+ }
+
+
+ /**
+ * Sets the handler to use when an external URL is clicked in the
+ * description window. This handler can perform some action, such as
+ * open the URL in a web browser. The default implementation will open
+ * the URL in a browser, but only if running in Java 6. If you want
+ * browser support for Java 5 and below, or otherwise want to respond to
+ * hyperlink clicks, you will have to install your own handler to do so.
+ *
+ * @param handler The new handler.
+ * @see #getExternalURLHandler()
+ */
+ public void setExternalURLHandler(ExternalURLHandler handler) {
+ this.externalURLHandler = handler;
+ }
+
+
+ /**
+ * Sets the redirector for external URL's found in code completion
+ * documentation. When a non-local link in completion popups is clicked,
+ * this redirector is given the chance to modify the URL fetched and
+ * displayed.
+ *
+ * @param redirector The link redirector, or null
for
+ * none.
+ * @see #getLinkRedirector()
+ */
+ public static void setLinkRedirector(LinkRedirector redirector) {
+ linkRedirector = redirector;
+ }
+
+
+ /**
+ * Sets the default list cell renderer to use when a completion provider
+ * does not supply its own.
+ *
+ * @param renderer The renderer to use. If this is null
,
+ * a default renderer is used.
+ * @see #getListCellRenderer()
+ */
+ public void setListCellRenderer(ListCellRenderer renderer) {
+ this.renderer = renderer;
+ if (popupWindow != null) {
+ popupWindow.setListCellRenderer(renderer);
+ hidePopupWindow();
+ }
+ }
+
+
+ /**
+ * Sets the renderer to use for {@link Completion}s in the optional
+ * parameter choices popup window (displayed when a
+ * {@link ParameterizedCompletion} is code-completed). If this isn't set,
+ * a default renderer is used.
+ *
+ * @param r The renderer to use.
+ * @see #getParamChoicesRenderer()
+ * @see #setParameterAssistanceEnabled(boolean)
+ */
+ public void setParamChoicesRenderer(ListCellRenderer r) {
+ paramChoicesRenderer = r;
+ }
+
+
+ /**
+ * Sets whether parameter assistance is enabled. If parameter assistance
+ * is enabled, and a "parameterized" completion (such as a function or
+ * method) is inserted, the user will get "assistance" in inserting the
+ * parameters in the form of a popup window with documentation and easy
+ * tabbing through the arguments (as seen in Eclipse and NetBeans).
+ *
+ * @param enabled Whether parameter assistance should be enabled.
+ * @see #isParameterAssistanceEnabled()
+ */
+ public void setParameterAssistanceEnabled(boolean enabled) {
+ parameterAssistanceEnabled = enabled;
+ }
+
+
+ /**
+ * Sets whether the "description window" should be shown beside the
+ * completion window.
+ *
+ * @param show Whether to show the description window.
+ * @see #isShowDescWindow()
+ */
+ public void setShowDescWindow(boolean show) {
+ hidePopupWindow(); // Needed to force it to take effect
+ showDescWindow = show;
+ }
+
+
+ /**
+ * Sets the keystroke that should be used to trigger the auto-complete
+ * popup window.
+ *
+ * @param ks The keystroke.
+ * @throws IllegalArgumentException If ks
is null
.
+ * @see #getTriggerKey()
+ */
+ public void setTriggerKey(KeyStroke ks) {
+ if (ks == null) {
+ throw new IllegalArgumentException("trigger key cannot be null");
+ }
+ if (!ks.equals(trigger)) {
+ if (textComponent != null) {
+ // Put old trigger action back.
+ uninstallTriggerKey();
+ // Grab current action for new trigger and replace it.
+ installTriggerKey(ks);
+ }
+ trigger = ks;
+ }
+ }
+
+
+ /**
+ * Uninstalls this auto-completion from its text component. If it is not
+ * installed on any text component, nothing happens.
+ *
+ * @see #install(JTextComponent)
+ */
+ public void uninstall() {
+
+ if (textComponent != null) {
+
+ hidePopupWindow(); // Unregisters listeners, actions, etc.
+
+ uninstallTriggerKey();
+
+ // Uninstall the function completion key.
+ char start = provider.getParameterListStart();
+ if (start != 0) {
+ KeyStroke ks = KeyStroke.getKeyStroke(start);
+ InputMap im = textComponent.getInputMap();
+ im.put(ks, oldParenKey);
+ ActionMap am = textComponent.getActionMap();
+ am.put(PARAM_COMPLETE_KEY, oldParenAction);
+ }
+
+ textComponentListener.removeFrom(textComponent);
+ if (parentWindow != null) {
+ parentWindowListener.removeFrom(parentWindow);
+ }
+
+ if (isAutoActivationEnabled()) {
+ autoActivationListener.removeFrom(textComponent);
+ }
+
+ UIManager.removePropertyChangeListener(lafListener);
+
+ textComponent = null;
+ popupWindow = null;
+
+ }
+
+ }
+
+
+ /**
+ * Replaces the "trigger key" action with the one that was there
+ * before auto-completion was installed.
+ */
+ private void uninstallTriggerKey() {
+ InputMap im = textComponent.getInputMap();
+ im.put(trigger, oldTriggerKey);
+ ActionMap am = textComponent.getActionMap();
+ am.put(PARAM_TRIGGER_KEY, oldTriggerAction);
+ }
+
+
+ /**
+ * Updates the LookAndFeel of the popup window. Applications can call
+ * this method as appropriate if they support changing the LookAndFeel
+ * at runtime.
+ */
+ private void updateUI() {
+ if (popupWindow != null) {
+ popupWindow.updateUI();
+ }
+ if (pcc != null) {
+ pcc.updateUI();
+ }
+ // Will practically always be a JComponent (a JLabel)
+ if (paramChoicesRenderer instanceof JComponent) {
+ ((JComponent) paramChoicesRenderer).updateUI();
+ }
+ }
+
+
+ /**
+ * Listens for events in the text component to auto-activate the code
+ * completion popup.
+ */
+ private class AutoActivationListener extends FocusAdapter
+ implements DocumentListener, CaretListener, ActionListener {
+
+ private Timer timer;
+ private boolean justInserted;
+
+ public AutoActivationListener() {
+ timer = new Timer(200, this);
+ timer.setRepeats(false);
+ }
+
+ public void actionPerformed(ActionEvent e) {
+ doCompletion();
+ }
+
+ public void addTo(JTextComponent tc) {
+ tc.addFocusListener(this);
+ tc.getDocument().addDocumentListener(this);
+ tc.addCaretListener(this);
+ }
+
+ public void caretUpdate(CaretEvent e) {
+ if (justInserted) {
+ justInserted = false;
+ } else {
+ timer.stop();
+ }
+ }
+
+ public void changedUpdate(DocumentEvent e) {
+ // Ignore
+ }
+
+ @Override
+ public void focusLost(FocusEvent e) {
+ timer.stop();
+ //hideChildWindows(); Other listener will do this
+ }
+
+ public void insertUpdate(DocumentEvent e) {
+ justInserted = false;
+ if (isAutoCompleteEnabled() && isAutoActivationEnabled() &&
+ e.getLength() == 1) {
+ if (provider.isAutoActivateOkay(textComponent)) {
+ timer.restart();
+ justInserted = true;
+ } else {
+ timer.stop();
+ }
+ } else {
+ timer.stop();
+ }
+ }
+
+ public void removeFrom(JTextComponent tc) {
+ tc.removeFocusListener(this);
+ tc.getDocument().removeDocumentListener(this);
+ tc.removeCaretListener(this);
+ timer.stop();
+ justInserted = false;
+ }
+
+ public void removeUpdate(DocumentEvent e) {
+ timer.stop();
+ }
+
+ }
+
+
+ /**
+ * The Action
that displays the popup window if
+ * auto-completion is enabled.
+ */
+ private class AutoCompleteAction extends AbstractAction {
+
+ public void actionPerformed(ActionEvent e) {
+ if (isAutoCompleteEnabled()) {
+ refreshPopupWindow();
+ } else if (oldTriggerAction != null) {
+ oldTriggerAction.actionPerformed(e);
+ }
+ }
+
+ }
+
+
+ /**
+ * Listens for LookAndFeel changes and updates the various popup windows
+ * involved in auto-completion accordingly.
+ */
+ private class LookAndFeelChangeListener implements PropertyChangeListener {
+
+ public void propertyChange(PropertyChangeEvent e) {
+ String name = e.getPropertyName();
+ if ("lookAndFeel".equals(name)) {
+ updateUI();
+ }
+ }
+
+ }
+
+
+ /**
+ * Action that starts a parameterized completion, e.g. after '(' is
+ * typed.
+ */
+ private class ParameterizedCompletionStartAction extends AbstractAction {
+
+ private String start;
+
+ public ParameterizedCompletionStartAction(char ch) {
+ this.start = Character.toString(ch);
+ }
+
+ public void actionPerformed(ActionEvent e) {
+
+ // Prevents keystrokes from messing up
+ boolean wasVisible = hidePopupWindow();
+
+ // Only proceed if they were selecting a completion
+ if (!wasVisible || !isParameterAssistanceEnabled()) {
+ textComponent.replaceSelection(start);
+ return;
+ }
+
+ Completion c = popupWindow.getSelection();
+ if (c instanceof ParameterizedCompletion) { // Should always be true
+ // Fixes capitalization of the entered text.
+ insertCompletion(c, true);
+ }
+
+ }
+
+ }
+
+
+ /**
+ * Listens for events in the parent window of the text component with
+ * auto-completion enabled.
+ */
+ private class ParentWindowListener extends ComponentAdapter
+ implements WindowFocusListener {
+
+ public void addTo(Window w) {
+ w.addComponentListener(this);
+ w.addWindowFocusListener(this);
+ }
+
+ @Override
+ public void componentHidden(ComponentEvent e) {
+ hideChildWindows();
+ }
+
+ @Override
+ public void componentMoved(ComponentEvent e) {
+ hideChildWindows();
+ }
+
+ @Override
+ public void componentResized(ComponentEvent e) {
+ hideChildWindows();
+ }
+
+ public void removeFrom(Window w) {
+ w.removeComponentListener(this);
+ w.removeWindowFocusListener(this);
+ }
+
+ public void windowGainedFocus(WindowEvent e) {
+ }
+
+ public void windowLostFocus(WindowEvent e) {
+ hideChildWindows();
+ }
+
+ }
+
+
+ /**
+ * Listens for events from the text component we're installed on.
+ */
+ private class TextComponentListener extends FocusAdapter
+ implements HierarchyListener {
+
+ void addTo(JTextComponent tc) {
+ tc.addFocusListener(this);
+ tc.addHierarchyListener(this);
+ }
+
+ /**
+ * Hide the auto-completion windows when the text component loses
+ * focus.
+ */
+ @Override
+ public void focusLost(FocusEvent e) {
+ hideChildWindows();
+ }
+
+ /**
+ * Called when the component hierarchy for our text component changes.
+ * When the text component is added to a new {@link Window}, this
+ * method registers listeners on that Window
.
+ *
+ * @param e The event.
+ */
+ public void hierarchyChanged(HierarchyEvent e) {
+
+ // NOTE: e many be null as we call this method at other times.
+ //System.out.println("Hierarchy changed! " + e);
+
+ Window oldParentWindow = parentWindow;
+ parentWindow = SwingUtilities.getWindowAncestor(textComponent);
+ if (parentWindow != oldParentWindow) {
+ if (oldParentWindow != null) {
+ parentWindowListener.removeFrom(oldParentWindow);
+ }
+ if (parentWindow != null) {
+ parentWindowListener.addTo(parentWindow);
+ }
+ }
+
+ }
+
+ public void removeFrom(JTextComponent tc) {
+ tc.removeFocusListener(this);
+ tc.removeHierarchyListener(this);
+ }
+
+ }
+
+ public void installExtraRefreshComponent(AutoCompleteExtraRefreshComponent jComp) {
+ area = jComp;
+ }
+
+
+ /**
+ * Displays a "tool tip" detailing the inputs to the function just entered.
+ *
+ * @param pc The completion.
+ * @param typedParamListStartChar Whether the parameterized completion list
+ * starting character was typed.
+ */
+ protected void startParameterizedCompletionAssistance(
+ ParameterizedCompletion pc, boolean typedParamListStartChar) {
+
+ // Get rid of the previous tool tip window, if there is one.
+ hideParameterCompletionPopups();
+
+ // Don't bother with a tool tip if there are no parameters, but if
+ // they typed e.g. the opening '(', make them overtype the ')'.
+ if (pc.getParamCount() == 0 && !(pc instanceof TemplateCompletion)) {
+ CompletionProvider p = pc.getProvider();
+ char end = p.getParameterListEnd(); // Might be '\0'
+ String text = end == '\0' ? "" : Character.toString(end);
+ if (typedParamListStartChar) {
+ String template = "${}" + text + "${cursor}";
+ textComponent.replaceSelection(Character.toString(p.getParameterListStart()));
+ TemplateCompletion tc = new TemplateCompletion(p, null, null, template);
+ pc = tc;
+ } else {
+ text = p.getParameterListStart() + text;
+ textComponent.replaceSelection(text);
+ return;
+ }
+ }
+
+ pcc = new ParameterizedCompletionContext(parentWindow, this, pc);
+ pcc.activate();
+
+ }
+
+ protected int refreshPopupWindow() {
+ // A return value of null => don't suggest completions
+ String text = provider.getAlreadyEnteredText(textComponent);
+ if (text == null && !isPopupVisible()) {
+ return getLineOfCaret();
+ }
+ // If the popup is currently visible, and they type a space (or any
+ // character that resets the completion list to "all completions"),
+ // the popup window should be hidden instead of being reset to show
+ // everything.
+ int textLen = text == null ? 0 : text.length();
+ if (textLen == 0 && isPopupVisible()) {
+ hidePopupWindow();
+ return getLineOfCaret();
+ }
+ final List completions = provider.getCompletions(textComponent);
+ int count = completions.size();
+ if (needSetPopupWindow(count, textLen)) {
+ if (popupWindow == null) {
+ popupWindow = createAutoCompletePopupWindow();
+ }
+ popupWindow.installComp(area);
+ popupWindow.setCompletions(completions);
+ if (!popupWindow.isVisible()) {
+ Rectangle r = null;
+ try {
+ r = textComponent.modelToView(textComponent.getCaretPosition());
+ } catch (BadLocationException ble) {
+ return -1;
+ }
+ Point p = new Point(r.x, r.y);
+ SwingUtilities.convertPointToScreen(p, textComponent);
+ r.x = p.x;
+ r.y = p.y;
+ popupWindow.setLocationRelativeTo(r);
+ popupWindow.setVisible(true);
+ }
+ } else if (count == 1) { // !isPopupVisible && autoCompleteSingleChoices
+ SwingUtilities.invokeLater(new Runnable() {
+ public void run() {
+ insertCompletion(completions.get(0));
+ }
+ });
+ } else {
+ hidePopupWindow();
+ }
+ return getLineOfCaret();
+ }
+}
diff --git a/designer-base/src/main/java/com/fr/design/gui/autocomplete/FormulaAutoCompletePopupWindow.java b/designer-base/src/main/java/com/fr/design/gui/autocomplete/FormulaAutoCompletePopupWindow.java
index 0e1c7c6ac..daed7cd4c 100644
--- a/designer-base/src/main/java/com/fr/design/gui/autocomplete/FormulaAutoCompletePopupWindow.java
+++ b/designer-base/src/main/java/com/fr/design/gui/autocomplete/FormulaAutoCompletePopupWindow.java
@@ -9,41 +9,7 @@
*/
package com.fr.design.gui.autocomplete;
-import com.fr.design.formula.FormulaPane;
-import com.fr.design.gui.syntax.ui.rsyntaxtextarea.PopupWindowDecorator;
-import com.fr.log.FineLoggerFactory;
-
-import javax.swing.AbstractAction;
-import javax.swing.Action;
-import javax.swing.ActionMap;
-import javax.swing.InputMap;
-import javax.swing.JList;
-import javax.swing.JPanel;
-import javax.swing.JScrollPane;
-import javax.swing.JWindow;
-import javax.swing.KeyStroke;
-import javax.swing.ListCellRenderer;
-import javax.swing.SwingUtilities;
-import javax.swing.event.CaretEvent;
-import javax.swing.event.CaretListener;
-import javax.swing.event.ListSelectionEvent;
-import javax.swing.event.ListSelectionListener;
-import javax.swing.plaf.ListUI;
-import javax.swing.text.Caret;
-import javax.swing.text.JTextComponent;
-import java.awt.BorderLayout;
-import java.awt.ComponentOrientation;
-import java.awt.Dimension;
-import java.awt.Point;
-import java.awt.Rectangle;
-import java.awt.Toolkit;
import java.awt.Window;
-import java.awt.event.ActionEvent;
-import java.awt.event.KeyEvent;
-import java.awt.event.MouseEvent;
-import java.awt.event.MouseListener;
-import java.util.List;
-
/**
* The actual popup window of choices. When visible, this window intercepts
@@ -55,949 +21,11 @@ import java.util.List;
* @author Robert Futrell
* @version 1.0
*/
-class FormulaAutoCompletePopupWindow extends JWindow implements CaretListener,
- ListSelectionListener, MouseListener {
-
- private final static int DIS = 5;
- /**
- * The parent AutoCompletion instance.
- */
- private FormulaPaneAutoCompletion ac;
-
- /**
- * The list of completion choices.
- */
- private JList list;
-
- /**
- * The contents of {@link #list()}.
- */
- private CompletionListModel model;
-
- /**
- * A hack to work around the fact that we clear our completion model (and
- * our selection) when hiding the completion window. This allows us to
- * still know what the user selected after the popup is hidden.
- */
- private Completion lastSelection;
-
- /**
- * Optional popup window containing a description of the currently
- * selected completion.
- */
- private AutoCompleteDescWindow descWindow;
-
- /**
- * The preferred size of the optional description window. This field
- * only exists because the user may (and usually will) set the size of
- * the description window before it exists (it must be parented to a
- * Window).
- */
- private Dimension preferredDescWindowSize;
-
- private FormulaPane.VariableTreeAndDescriptionArea component;
-
- /**
- * Whether the completion window and the optional description window
- * should be displayed above the current caret position (as opposed to
- * underneath it, which is preferred unless there is not enough space).
- */
- private boolean aboveCaret;
-
- private int lastLine;
-
- private KeyActionPair escapeKap;
- private KeyActionPair upKap;
- private KeyActionPair downKap;
- private KeyActionPair leftKap;
- private KeyActionPair rightKap;
- private KeyActionPair enterKap;
- private KeyActionPair tabKap;
- private KeyActionPair homeKap;
- private KeyActionPair endKap;
- private KeyActionPair pageUpKap;
- private KeyActionPair pageDownKap;
- private KeyActionPair ctrlCKap;
-
- private KeyActionPair oldEscape, oldUp, oldDown, oldLeft, oldRight,
- oldEnter, oldTab, oldHome, oldEnd, oldPageUp, oldPageDown,
- oldCtrlC;
-
- /**
- * The space between the caret and the completion popup.
- */
- private static final int VERTICAL_SPACE = 1;
-
- /**
- * The class name of the Substance List UI.
- */
- private static final String SUBSTANCE_LIST_UI =
- "org.pushingpixels.substance.internal.ui.SubstanceListUI";
+class FormulaAutoCompletePopupWindow extends AutoCompleteWithExtraRefreshPopupWindow {
- /**
- * Constructor.
- *
- * @param parent The parent window (hosting the text component).
- * @param ac The auto-completion instance.
- */
public FormulaAutoCompletePopupWindow(Window parent, final FormulaPaneAutoCompletion ac) {
-
- super(parent);
- ComponentOrientation o = ac.getTextComponentOrientation();
-
- this.ac = ac;
- model = new CompletionListModel();
- list = new PopupList(model);
-
- list.setCellRenderer(new DelegatingCellRenderer());
- list.addListSelectionListener(this);
- list.addMouseListener(this);
-
- JPanel contentPane = new JPanel(new BorderLayout());
- JScrollPane sp = new JScrollPane(list,
- JScrollPane.VERTICAL_SCROLLBAR_ALWAYS,
- JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS);
-
- // In 1.4, JScrollPane.setCorner() has a bug where it won't accept
- // JScrollPane.LOWER_TRAILING_CORNER, even though that constant is
- // defined. So we have to put the logic added in 1.5 to handle it
- // here.
- JPanel corner = new SizeGrip();
- //sp.setCorner(JScrollPane.LOWER_TRAILING_CORNER, corner);
- boolean isLeftToRight = o.isLeftToRight();
- String str = isLeftToRight ? JScrollPane.LOWER_RIGHT_CORNER :
- JScrollPane.LOWER_LEFT_CORNER;
- sp.setCorner(str, corner);
-
- contentPane.add(sp);
- setContentPane(contentPane);
- applyComponentOrientation(o);
-
- // Give apps a chance to decorate us with drop shadows, etc.
- if (Util.getShouldAllowDecoratingMainAutoCompleteWindows()) {
- PopupWindowDecorator decorator = PopupWindowDecorator.get();
- if (decorator != null) {
- decorator.decorate(this);
- }
- }
-
- pack();
-
- setFocusableWindowState(false);
-
- lastLine = -1;
-
- }
-
-
- public void caretUpdate(CaretEvent e) {
- if (isVisible()) { // Should always be true
- int line = ac.getLineOfCaret();
- if (line != lastLine) {
- lastLine = -1;
- setVisible(false);
- } else {
- doAutocomplete();
- }
- } else if (AutoCompletion.isDebug()) {
- Thread.dumpStack();
- }
- }
-
-
- /**
- * Creates the description window.
- *
- * @return The description window.
- */
- private AutoCompleteDescWindow createDescriptionWindow() {
- AutoCompleteDescWindow dw = new AutoCompleteDescWindow(this, ac);
- dw.applyComponentOrientation(ac.getTextComponentOrientation());
- Dimension size = preferredDescWindowSize;
- if (size == null) {
- size = getSize();
- }
- dw.setSize(size);
- return dw;
- }
-
-
- /**
- * Creates the mappings from keys to Actions we'll be putting into the
- * text component's ActionMap and InputMap.
- */
- private void createKeyActionPairs() {
-
- // Actions we'll install.
- EnterAction enterAction = new EnterAction();
- escapeKap = new KeyActionPair("Escape", new EscapeAction());
- upKap = new KeyActionPair("Up", new UpAction());
- downKap = new KeyActionPair("Down", new DownAction());
- leftKap = new KeyActionPair("Left", new LeftAction());
- rightKap = new KeyActionPair("Right", new RightAction());
- enterKap = new KeyActionPair("Enter", enterAction);
- tabKap = new KeyActionPair("Tab", enterAction);
- homeKap = new KeyActionPair("Home", new HomeAction());
- endKap = new KeyActionPair("End", new EndAction());
- pageUpKap = new KeyActionPair("PageUp", new PageUpAction());
- pageDownKap = new KeyActionPair("PageDown", new PageDownAction());
- ctrlCKap = new KeyActionPair("CtrlC", new CopyAction());
-
- // Buffers for the actions we replace.
- oldEscape = new KeyActionPair();
- oldUp = new KeyActionPair();
- oldDown = new KeyActionPair();
- oldLeft = new KeyActionPair();
- oldRight = new KeyActionPair();
- oldEnter = new KeyActionPair();
- oldTab = new KeyActionPair();
- oldHome = new KeyActionPair();
- oldEnd = new KeyActionPair();
- oldPageUp = new KeyActionPair();
- oldPageDown = new KeyActionPair();
- oldCtrlC = new KeyActionPair();
-
- }
-
-
- protected void doAutocomplete() {
- lastLine = ac.refreshPopupWindow();
- }
-
-
- /**
- * Returns the copy keystroke to use for this platform.
- *
- * @return The copy keystroke.
- */
- private static final KeyStroke getCopyKeyStroke() {
- int key = KeyEvent.VK_C;
- int mask = Toolkit.getDefaultToolkit().getMenuShortcutKeyMask();
- return KeyStroke.getKeyStroke(key, mask);
- }
-
-
- /**
- * Returns the default list cell renderer used when a completion provider
- * does not supply its own.
- *
- * @return The default list cell renderer.
- * @see #setListCellRenderer(ListCellRenderer)
- */
- public ListCellRenderer getListCellRenderer() {
- DelegatingCellRenderer dcr = (DelegatingCellRenderer) list.
- getCellRenderer();
- return dcr.getFallbackCellRenderer();
- }
-
-
- /**
- * Returns the selected value, or null
if nothing is selected.
- *
- * @return The selected value.
- */
- public Completion getSelection() {
- return isShowing() ? (Completion) list.getSelectedValue() : lastSelection;
- }
-
-
- /**
- * Inserts the currently selected completion.
- *
- * @see #getSelection()
- */
- private void insertSelectedCompletion() {
- Completion comp = getSelection();
- ac.insertCompletion(comp);
- }
-
- public void installComp(FormulaPane.VariableTreeAndDescriptionArea component) {
- this.component = component;
- }
-
- private void refreshInstallComp() {
- component.refreshText(getSelection().getReplacementText());
- }
-
-
- /**
- * Registers keyboard actions to listen for in the text component and
- * intercept.
- *
- * @see #uninstallKeyBindings()
- */
- private void installKeyBindings() {
-
- if (AutoCompletion.isDebug()) {
- FineLoggerFactory.getLogger().debug("PopupWindow: Installing keybindings");
- }
-
- if (escapeKap == null) { // Lazily create actions.
- createKeyActionPairs();
- }
-
- JTextComponent comp = ac.getTextComponent();
- InputMap im = comp.getInputMap();
- ActionMap am = comp.getActionMap();
-
- replaceAction(im, am, KeyEvent.VK_ESCAPE, escapeKap, oldEscape);
- if (AutoCompletion.isDebug() && oldEscape.action == escapeKap.action) {
- Thread.dumpStack();
- }
- replaceAction(im, am, KeyEvent.VK_UP, upKap, oldUp);
- replaceAction(im, am, KeyEvent.VK_LEFT, leftKap, oldLeft);
- replaceAction(im, am, KeyEvent.VK_DOWN, downKap, oldDown);
- replaceAction(im, am, KeyEvent.VK_RIGHT, rightKap, oldRight);
- replaceAction(im, am, KeyEvent.VK_ENTER, enterKap, oldEnter);
- replaceAction(im, am, KeyEvent.VK_TAB, tabKap, oldTab);
- replaceAction(im, am, KeyEvent.VK_HOME, homeKap, oldHome);
- replaceAction(im, am, KeyEvent.VK_END, endKap, oldEnd);
- replaceAction(im, am, KeyEvent.VK_PAGE_UP, pageUpKap, oldPageUp);
- replaceAction(im, am, KeyEvent.VK_PAGE_DOWN, pageDownKap, oldPageDown);
-
- // Make Ctrl+C copy from description window. This isn't done
- // automagically because the desc. window is not focusable, and copying
- // from text components can only be done from focused components.
- KeyStroke ks = getCopyKeyStroke();
- oldCtrlC.key = im.get(ks);
- im.put(ks, ctrlCKap.key);
- oldCtrlC.action = am.get(ctrlCKap.key);
- am.put(ctrlCKap.key, ctrlCKap.action);
-
- comp.addCaretListener(this);
-
- }
-
-
- public void mouseClicked(MouseEvent e) {
- if (e.getClickCount() == 2 && e.getButton() == 1) {
- insertSelectedCompletion();
- }
- }
-
-
- public void mouseEntered(MouseEvent e) {
- }
-
-
- public void mouseExited(MouseEvent e) {
- }
-
-
- public void mousePressed(MouseEvent e) {
- refreshInstallComp();
- }
-
-
- public void mouseReleased(MouseEvent e) {
- }
-
-
- /**
- * Positions the description window relative to the completion choices
- * window. We assume there is room on one side of the other for this
- * entire window to fit.
- */
- private void positionDescWindow() {
-
- boolean showDescWindow = descWindow != null && ac.isShowDescWindow();
- if (!showDescWindow) {
- return;
- }
-
- // Don't use getLocationOnScreen() as this throws an exception if
- // window isn't visible yet, but getLocation() doesn't, and is in
- // screen coordinates!
- Point p = getLocation();
- Rectangle screenBounds = Util.getScreenBoundsForPoint(p.x, p.y);
- //Dimension screenSize = getToolkit().getScreenSize();
- //int totalH = Math.max(getHeight(), descWindow.getHeight());
-
- // Try to position to the right first (LTR)
- int x;
- if (ac.getTextComponentOrientation().isLeftToRight()) {
- x = getX() + getWidth() + DIS;
- if (x + descWindow.getWidth() > screenBounds.x + screenBounds.width) { // doesn't fit
- x = getX() - DIS - descWindow.getWidth();
- }
- } else { // RTL
- x = getX() - DIS - descWindow.getWidth();
- if (x < screenBounds.x) { // Doesn't fit
- x = getX() + getWidth() + DIS;
- }
- }
-
- int y = getY();
- if (aboveCaret) {
- y = y + getHeight() - descWindow.getHeight();
- }
-
- if (x != descWindow.getX() || y != descWindow.getY()) {
- descWindow.setLocation(x, y);
- }
-
- }
-
-
- /**
- * "Puts back" the original key/Action pair for a keystroke. This is used
- * when this popup is hidden.
- *
- * @param im The input map.
- * @param am The action map.
- * @param key The keystroke whose key/Action pair to change.
- * @param kap The (original) key/Action pair.
- * @see #replaceAction(InputMap, ActionMap, int, KeyActionPair, KeyActionPair)
- */
- private void putBackAction(InputMap im, ActionMap am, int key,
- KeyActionPair kap) {
- KeyStroke ks = KeyStroke.getKeyStroke(key, 0);
- am.put(im.get(ks), kap.action); // Original action for the "new" key
- im.put(ks, kap.key); // Original key for the keystroke.
- }
-
-
- /**
- * Replaces a key/Action pair in an InputMap and ActionMap with a new
- * pair.
- *
- * @param im The input map.
- * @param am The action map.
- * @param key The keystroke whose information to replace.
- * @param kap The new key/Action pair for key
.
- * @param old A buffer in which to place the old key/Action pair.
- * @see #putBackAction(InputMap, ActionMap, int, KeyActionPair)
- */
- private void replaceAction(InputMap im, ActionMap am, int key,
- KeyActionPair kap, KeyActionPair old) {
- KeyStroke ks = KeyStroke.getKeyStroke(key, 0);
- old.key = im.get(ks);
- im.put(ks, kap.key);
- old.action = am.get(kap.key);
- am.put(kap.key, kap.action);
- }
-
-
- /**
- * Selects the first item in the completion list.
- *
- * @see #selectLastItem()
- */
- private void selectFirstItem() {
- if (model.getSize() > 0) {
- list.setSelectedIndex(0);
- list.ensureIndexIsVisible(0);
- }
- }
-
-
- /**
- * Selects the last item in the completion list.
- *
- * @see #selectFirstItem()
- */
- private void selectLastItem() {
- int index = model.getSize() - 1;
- if (index > -1) {
- list.setSelectedIndex(index);
- list.ensureIndexIsVisible(index);
- }
- }
-
-
- /**
- * Selects the next item in the completion list.
- *
- * @see #selectPreviousItem()
- */
- private void selectNextItem() {
- int index = list.getSelectedIndex();
- if (index > -1) {
- index = (index + 1) % model.getSize();
- list.setSelectedIndex(index);
- list.ensureIndexIsVisible(index);
- }
- }
-
-
- /**
- * Selects the completion item one "page down" from the currently selected
- * one.
- *
- * @see #selectPageUpItem()
- */
- private void selectPageDownItem() {
- int visibleRowCount = list.getVisibleRowCount();
- int i = Math.min(list.getModel().getSize() - 1,
- list.getSelectedIndex() + visibleRowCount);
- list.setSelectedIndex(i);
- list.ensureIndexIsVisible(i);
- }
-
-
- /**
- * Selects the completion item one "page up" from the currently selected
- * one.
- *
- * @see #selectPageDownItem()
- */
- private void selectPageUpItem() {
- int visibleRowCount = list.getVisibleRowCount();
- int i = Math.max(0, list.getSelectedIndex() - visibleRowCount);
- list.setSelectedIndex(i);
- list.ensureIndexIsVisible(i);
- }
-
-
- /**
- * Selects the previous item in the completion list.
- *
- * @see #selectNextItem()
- */
- private void selectPreviousItem() {
- int index = list.getSelectedIndex();
- switch (index) {
- case 0:
- index = list.getModel().getSize() - 1;
- break;
- case -1: // Check for an empty list (would be an error)
- index = list.getModel().getSize() - 1;
- if (index == -1) {
- return;
- }
- break;
- default:
- index = index - 1;
- break;
- }
- list.setSelectedIndex(index);
- list.ensureIndexIsVisible(index);
- }
-
-
- /**
- * Sets the completions to display in the choices list. The first
- * completion is selected.
- *
- * @param completions The completions to display.
- */
- public void setCompletions(List completions) {
- model.setContents(completions);
- selectFirstItem();
- }
-
-
- /**
- * Sets the size of the description window.
- *
- * @param size The new size. This cannot be null
.
- */
- public void setDescriptionWindowSize(Dimension size) {
- if (descWindow != null) {
- descWindow.setSize(size);
- } else {
- preferredDescWindowSize = size;
- }
- }
-
-
- /**
- * Sets the default list cell renderer to use when a completion provider
- * does not supply its own.
- *
- * @param renderer The renderer to use. If this is null
,
- * a default renderer is used.
- * @see #getListCellRenderer()
- */
- public void setListCellRenderer(ListCellRenderer renderer) {
- DelegatingCellRenderer dcr = (DelegatingCellRenderer) list.
- getCellRenderer();
- dcr.setFallbackCellRenderer(renderer);
- }
-
-
- /**
- * Sets the location of this window to be "good" relative to the specified
- * rectangle. That rectangle should be the location of the text
- * component's caret, in screen coordinates.
- *
- * @param r The text component's caret position, in screen coordinates.
- */
- public void setLocationRelativeTo(Rectangle r) {
-
- // Multi-monitor support - make sure the completion window (and
- // description window, if applicable) both fit in the same window in
- // a multi-monitor environment. To do this, we decide which monitor
- // the rectangle "r" is in, and use that one (just pick top-left corner
- // as the defining point).
- Rectangle screenBounds = Util.getScreenBoundsForPoint(r.x, r.y);
- //Dimension screenSize = getToolkit().getScreenSize();
-
- boolean showDescWindow = descWindow != null && ac.isShowDescWindow();
- int totalH = getHeight();
- if (showDescWindow) {
- totalH = Math.max(totalH, descWindow.getHeight());
- }
-
- // Try putting our stuff "below" the caret first. We assume that the
- // entire height of our stuff fits on the screen one way or the other.
- aboveCaret = false;
- int y = r.y + r.height + VERTICAL_SPACE;
- if (y + totalH > screenBounds.height) {
- y = r.y - VERTICAL_SPACE - getHeight();
- aboveCaret = true;
- }
-
- // Get x-coordinate of completions. Try to align left edge with the
- // caret first.
- int x = r.x;
- if (!ac.getTextComponentOrientation().isLeftToRight()) {
- x -= getWidth(); // RTL => align right edge
- }
- if (x < screenBounds.x) {
- x = screenBounds.x;
- } else if (x + getWidth() > screenBounds.x + screenBounds.width) { // completions don't fit
- x = screenBounds.x + screenBounds.width - getWidth();
- }
-
- setLocation(x, y);
-
- // Position the description window, if necessary.
- if (showDescWindow) {
- positionDescWindow();
- }
-
- }
-
-
- /**
- * Toggles the visibility of this popup window.
- *
- * @param visible Whether this window should be visible.
- */
- @Override
- public void setVisible(boolean visible) {
-
- if (visible != isVisible()) {
-
- if (visible) {
- installKeyBindings();
- lastLine = ac.getLineOfCaret();
- selectFirstItem();
- if (descWindow == null && ac.isShowDescWindow()) {
- descWindow = createDescriptionWindow();
- positionDescWindow();
- }
- // descWindow needs a kick-start the first time it's displayed.
- // Also, the newly-selected item in the choices list is
- // probably different from the previous one anyway.
- if (descWindow != null) {
- Completion c = (Completion) list.getSelectedValue();
- if (c != null) {
- descWindow.setDescriptionFor(c);
- }
- }
- } else {
- uninstallKeyBindings();
- }
-
- super.setVisible(visible);
-
- // Some languages, such as Java, can use quite a lot of memory
- // when displaying hundreds of completion choices. We pro-actively
- // clear our list model here to make them available for GC.
- // Otherwise, they stick around, and consider the following: a
- // user starts code-completion for Java 5 SDK classes, then hides
- // the dialog, then changes the "class path" to use a Java 6 SDK
- // instead. On pressing Ctrl+space, a new array of Completions is
- // created. If this window holds on to the previous Completions,
- // you're getting roughly 2x the necessary Completions in memory
- // until the Completions are actually passed to this window.
- if (!visible) { // Do after super.setVisible(false)
- lastSelection = (Completion) list.getSelectedValue();
- model.clear();
- }
-
- // Must set descWindow's visibility one way or the other each time,
- // 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.isShowDescWindow());
- }
-
- }
-
- }
-
-
- /**
- * Stops intercepting certain keystrokes from the text component.
- *
- * @see #installKeyBindings()
- */
- private void uninstallKeyBindings() {
-
- if (AutoCompletion.isDebug()) {
- FineLoggerFactory.getLogger().debug("PopupWindow: Removing keybindings");
- }
-
- JTextComponent comp = ac.getTextComponent();
- InputMap im = comp.getInputMap();
- ActionMap am = comp.getActionMap();
-
- putBackAction(im, am, KeyEvent.VK_ESCAPE, oldEscape);
- putBackAction(im, am, KeyEvent.VK_UP, oldUp);
- putBackAction(im, am, KeyEvent.VK_DOWN, oldDown);
- putBackAction(im, am, KeyEvent.VK_LEFT, oldLeft);
- putBackAction(im, am, KeyEvent.VK_RIGHT, oldRight);
- putBackAction(im, am, KeyEvent.VK_ENTER, oldEnter);
- putBackAction(im, am, KeyEvent.VK_TAB, oldTab);
- putBackAction(im, am, KeyEvent.VK_HOME, oldHome);
- putBackAction(im, am, KeyEvent.VK_END, oldEnd);
- putBackAction(im, am, KeyEvent.VK_PAGE_UP, oldPageUp);
- putBackAction(im, am, KeyEvent.VK_PAGE_DOWN, oldPageDown);
-
- // Ctrl+C
- KeyStroke ks = getCopyKeyStroke();
- am.put(im.get(ks), oldCtrlC.action); // Original action
- im.put(ks, oldCtrlC.key); // Original key
-
- comp.removeCaretListener(this);
-
- }
-
-
- /**
- * Updates the LookAndFeel of this window and the description
- * window.
- */
- public void updateUI() {
- SwingUtilities.updateComponentTreeUI(this);
- if (descWindow != null) {
- descWindow.updateUI();
- }
- }
-
-
- /**
- * Called when a new item is selected in the popup list.
- *
- * @param e The event.
- */
- public void valueChanged(ListSelectionEvent e) {
- if (!e.getValueIsAdjusting()) {
- Object value = list.getSelectedValue();
- if (value != null && descWindow != null) {
- descWindow.setDescriptionFor((Completion) value);
- positionDescWindow();
- }
- }
- }
-
-
- class CopyAction extends AbstractAction {
-
- public void actionPerformed(ActionEvent e) {
- boolean doNormalCopy = false;
- if (descWindow != null && descWindow.isVisible()) {
- doNormalCopy = !descWindow.copy();
- }
- if (doNormalCopy) {
- ac.getTextComponent().copy();
- }
- }
-
- }
-
-
- class DownAction extends AbstractAction {
-
- public void actionPerformed(ActionEvent e) {
- if (isVisible()) {
- selectNextItem();
- refreshInstallComp();
- }
- }
-
- }
-
-
- class EndAction extends AbstractAction {
-
- public void actionPerformed(ActionEvent e) {
- if (isVisible()) {
- selectLastItem();
- }
- }
-
- }
-
-
- class EnterAction extends AbstractAction {
-
- public void actionPerformed(ActionEvent e) {
- if (isVisible()) {
- insertSelectedCompletion();
- refreshInstallComp();
- }
- }
-
+ super(parent,ac);
}
-
- class EscapeAction extends AbstractAction {
-
- public void actionPerformed(ActionEvent e) {
- if (isVisible()) {
- setVisible(false);
- }
- }
-
- }
-
-
- class HomeAction extends AbstractAction {
-
- public void actionPerformed(ActionEvent e) {
- if (isVisible()) {
- selectFirstItem();
- }
- }
-
- }
-
-
- /**
- * A mapping from a key (an Object) to an Action.
- */
- private static class KeyActionPair {
-
- public Object key;
- public Action action;
-
- public KeyActionPair() {
- }
-
- public KeyActionPair(Object key, Action a) {
- this.key = key;
- this.action = a;
- }
-
- }
-
-
- class LeftAction extends AbstractAction {
-
- public void actionPerformed(ActionEvent e) {
- if (isVisible()) {
- JTextComponent comp = ac.getTextComponent();
- Caret c = comp.getCaret();
- int dot = c.getDot();
- if (dot > 0) {
- c.setDot(--dot);
- // Ensure moving left hasn't moved us up a line, thus
- // hiding the popup window.
- if (comp.isVisible()) {
- if (lastLine != -1) {
- doAutocomplete();
- }
- }
- }
- }
- }
-
- }
-
-
- class PageDownAction extends AbstractAction {
-
- public void actionPerformed(ActionEvent e) {
- if (isVisible()) {
- selectPageDownItem();
- }
- }
-
- }
-
-
- class PageUpAction extends AbstractAction {
-
- public void actionPerformed(ActionEvent e) {
- if (isVisible()) {
- selectPageUpItem();
- }
- }
-
- }
-
-
- /**
- * The actual list of completion choices in this popup window.
- */
- private class PopupList extends JList {
-
- public PopupList(CompletionListModel model) {
- super(model);
- }
-
- @Override
- public void setUI(ListUI ui) {
- if (Util.getUseSubstanceRenderers() &&
- SUBSTANCE_LIST_UI.equals(ui.getClass().getName())) {
- // Substance requires its special ListUI be installed for
- // its renderers to actually render (!), but long completion
- // lists (e.g. PHPCompletionProvider in RSTALanguageSupport)
- // will simply populate too slowly on initial display (when
- // calculating preferred size of all items), so in this case
- // we give a prototype cell value.
- CompletionProvider p = ac.getCompletionProvider();
- BasicCompletion bc = new BasicCompletion(p, "Hello world");
- setPrototypeCellValue(bc);
- } else {
- // Our custom UI that is faster for long HTML completion lists.
- ui = new FastListUI();
- setPrototypeCellValue(null);
- }
- super.setUI(ui);
- }
-
- }
-
-
- class RightAction extends AbstractAction {
-
- public void actionPerformed(ActionEvent e) {
- if (isVisible()) {
- JTextComponent comp = ac.getTextComponent();
- Caret c = comp.getCaret();
- int dot = c.getDot();
- if (dot < comp.getDocument().getLength()) {
- c.setDot(++dot);
- // Ensure moving right hasn't moved us up a line, thus
- // hiding the popup window.
- if (comp.isVisible()) {
- if (lastLine != -1) {
- doAutocomplete();
- }
- }
- }
- }
- }
-
- }
-
-
- class UpAction extends AbstractAction {
-
- public void actionPerformed(ActionEvent e) {
- if (isVisible()) {
- selectPreviousItem();
- refreshInstallComp();
- }
- }
-
- }
-
-
}
\ No newline at end of file
diff --git a/designer-base/src/main/java/com/fr/design/gui/autocomplete/FormulaPaneAutoCompletion.java b/designer-base/src/main/java/com/fr/design/gui/autocomplete/FormulaPaneAutoCompletion.java
index 8168c767d..cd37fd9bc 100644
--- a/designer-base/src/main/java/com/fr/design/gui/autocomplete/FormulaPaneAutoCompletion.java
+++ b/designer-base/src/main/java/com/fr/design/gui/autocomplete/FormulaPaneAutoCompletion.java
@@ -2,226 +2,18 @@ package com.fr.design.gui.autocomplete;
import com.fr.design.formula.FormulaPane;
-import javax.swing.AbstractAction;
-import javax.swing.Action;
-import javax.swing.ActionMap;
-import javax.swing.InputMap;
-import javax.swing.JComponent;
import javax.swing.KeyStroke;
import javax.swing.ListCellRenderer;
-import javax.swing.SwingUtilities;
-import javax.swing.Timer;
-import javax.swing.UIManager;
-import javax.swing.event.CaretEvent;
-import javax.swing.event.CaretListener;
-import javax.swing.event.DocumentEvent;
-import javax.swing.event.DocumentListener;
-import javax.swing.text.BadLocationException;
import javax.swing.text.Caret;
-import javax.swing.text.Document;
-import javax.swing.text.Element;
import javax.swing.text.JTextComponent;
-import java.awt.ComponentOrientation;
-import java.awt.Dimension;
-import java.awt.Point;
-import java.awt.Rectangle;
import java.awt.Window;
-import java.awt.event.ActionEvent;
-import java.awt.event.ActionListener;
-import java.awt.event.ComponentAdapter;
-import java.awt.event.ComponentEvent;
-import java.awt.event.FocusAdapter;
-import java.awt.event.FocusEvent;
-import java.awt.event.HierarchyEvent;
-import java.awt.event.HierarchyListener;
-import java.awt.event.KeyEvent;
-import java.awt.event.WindowEvent;
-import java.awt.event.WindowFocusListener;
-import java.beans.PropertyChangeEvent;
-import java.beans.PropertyChangeListener;
-import java.util.List;
-
-import static com.fr.design.gui.syntax.ui.rtextarea.RTADefaultInputMap.DEFAULT_MODIFIER;
/**
* @author Hoky
* @date 2021/11/2
* @description 重写一个支持刷新公式树组件的AutoCompletion
*/
-public class FormulaPaneAutoCompletion extends AutoCompletion {
- private FormulaPane.VariableTreeAndDescriptionArea area;
-
- /**
- * The text component we're providing completion for.
- */
- private JTextComponent textComponent;
-
- /**
- * The parent window of {@link #textComponent}.
- */
- private Window parentWindow;
-
- /**
- * The popup window containing completion choices.
- */
- private FormulaAutoCompletePopupWindow popupWindow;
-
- /**
- * The preferred size of the completion choices window. This field exists
- * because the user will likely set the preferred size of the window
- * before it is actually created.
- */
- private Dimension preferredChoicesWindowSize;
-
- /**
- * The preferred size of the optional description window. This field
- * only exists because the user may (and usually will) set the size of
- * the description window before it exists (it must be parented to a
- * Window).
- */
- private Dimension preferredDescWindowSize;
-
- /**
- * Manages any parameterized completions that are inserted.
- */
- private ParameterizedCompletionContext pcc;
-
- /**
- * Provides the completion options relevant to the current caret position.
- */
- private CompletionProvider provider;
-
- /**
- * The renderer to use for the completion choices. If this is
- * null
, then a default renderer is used.
- */
- private ListCellRenderer renderer;
-
- /**
- * The handler to use when an external URL is clicked in the help
- * documentation.
- */
- private ExternalURLHandler externalURLHandler;
-
- /**
- * An optional redirector that converts URL's to some other location before
- * being handed over to externalURLHandler
.
- */
- private static LinkRedirector linkRedirector;
-
- /**
- * Whether the description window should be displayed along with the
- * completion choice window.
- */
- private boolean showDescWindow;
-
- /**
- * Whether auto-complete is enabled.
- */
- private boolean autoCompleteEnabled;
-
- /**
- * Whether the auto-activation of auto-complete (after a delay, after the
- * user types an appropriate character) is enabled.
- */
- private boolean autoActivationEnabled;
-
- /**
- * Whether or not, when there is only a single auto-complete option
- * that matches the text at the current text position, that text should
- * be auto-inserted, instead of the completion window displaying.
- */
- private boolean autoCompleteSingleChoices;
-
- /**
- * Whether parameter assistance is enabled.
- */
- private boolean parameterAssistanceEnabled;
-
- /**
- * A renderer used for {@link Completion}s in the optional parameter
- * choices popup window (displayed when a {@link ParameterizedCompletion}
- * is code-completed). If this isn't set, a default renderer is used.
- */
- private ListCellRenderer paramChoicesRenderer;
-
- /**
- * The keystroke that triggers the completion window.
- */
- private KeyStroke trigger;
-
- /**
- * The previous key in the text component's InputMap
for the
- * trigger key.
- */
- private Object oldTriggerKey;
-
- /**
- * The action previously assigned to {@link #trigger}, so we can reset it
- * if the user disables auto-completion.
- */
- private Action oldTriggerAction;
-
- /**
- * The previous key in the text component's InputMap
for the
- * parameter completion trigger key.
- */
- private Object oldParenKey;
-
- /**
- * The action previously assigned to the parameter completion key, so we
- * can reset it when we uninstall.
- */
- private Action oldParenAction;
-
- /**
- * Listens for events in the parent window that affect the visibility of
- * the popup windows.
- */
- private ParentWindowListener parentWindowListener;
-
- /**
- * Listens for events from the text component that affect the visibility
- * of the popup windows.
- */
- private TextComponentListener textComponentListener;
-
- /**
- * Listens for events in the text component that cause the popup windows
- * to automatically activate.
- */
- private AutoActivationListener autoActivationListener;
-
- /**
- * Listens for LAF changes so the auto-complete windows automatically
- * update themselves accordingly.
- */
- private LookAndFeelChangeListener lafListener;
-
- /**
- * The key used in the input map for the AutoComplete action.
- */
- private static final String PARAM_TRIGGER_KEY = "AutoComplete";
-
- /**
- * Key used in the input map for the parameter completion action.
- */
- private static final String PARAM_COMPLETE_KEY = "AutoCompletion.FunctionStart";
-
- /**
- * Stores how to render auto-completion-specific highlights in text
- * components.
- */
- private static final AutoCompletionStyleContext STYLE_CONTEXT =
- new AutoCompletionStyleContext();
-
- /**
- * Whether debug messages should be printed to stdout as AutoCompletion
- * runs.
- */
- private static final boolean DEBUG = initDebug();
-
-
+public class FormulaPaneAutoCompletion extends AutoCompletionWithExtraRefresh {
/**
* Constructor.
*
@@ -230,957 +22,29 @@ public class FormulaPaneAutoCompletion extends AutoCompletion {
*/
public FormulaPaneAutoCompletion(CompletionProvider provider) {
super(provider);
- setChoicesWindowSize(350, 200);
- setDescriptionWindowSize(350, 250);
-
- setCompletionProvider(provider);
- setTriggerKey(getDefaultTriggerKey());
- setAutoCompleteEnabled(true);
- setAutoCompleteSingleChoices(true);
- setAutoActivationEnabled(false);
- setShowDescWindow(false);
- parentWindowListener = new ParentWindowListener();
- textComponentListener = new TextComponentListener();
- autoActivationListener = new AutoActivationListener();
- lafListener = new LookAndFeelChangeListener();
- }
-
-
- /**
- * Displays the popup window. Hosting applications can call this method
- * to programmatically begin an auto-completion operation.
- */
- public void doCompletion() {
- refreshPopupWindow();
- }
-
-
- /**
- * Returns the delay between when the user types a character and when the
- * code completion popup should automatically appear (if applicable).
- *
- * @return The delay, in milliseconds.
- * @see #setAutoActivationDelay(int)
- */
- public int getAutoActivationDelay() {
- return autoActivationListener.timer.getDelay();
}
-
- /**
- * Returns whether, if a single auto-complete choice is available, it
- * should be automatically inserted, without displaying the popup menu.
- *
- * @return Whether to auto-complete single choices.
- * @see #setAutoCompleteSingleChoices(boolean)
- */
- public boolean isAutoCompleteSingleChoices() {
- return autoCompleteSingleChoices;
- }
-
-
- /**
- * Returns the completion provider.
- *
- * @return The completion provider.
- */
- public CompletionProvider getCompletionProvider() {
- return provider;
- }
-
-
- /**
- * Returns whether debug is enabled for AutoCompletion.
- *
- * @return Whether debug is enabled.
- */
- static boolean isDebug() {
- return DEBUG;
- }
-
-
- /**
- * Returns the default auto-complete "trigger key" for this OS. For
- * Windows, for example, it is Ctrl+Space.
- *
- * @return The default auto-complete trigger key.
- */
- public static KeyStroke getDefaultTriggerKey() {
- // Default to CTRL, even on Mac, since Ctrl+Space activates Spotlight
- return KeyStroke.getKeyStroke(KeyEvent.VK_SPACE, DEFAULT_MODIFIER);
- }
-
-
- /**
- * Returns the handler to use when an external URL is clicked in the
- * description window.
- *
- * @return The handler.
- * @see #setExternalURLHandler(ExternalURLHandler)
- * @see #getLinkRedirector()
- */
- public ExternalURLHandler getExternalURLHandler() {
- return externalURLHandler;
- }
-
-
- int getLineOfCaret() {
- Document doc = textComponent.getDocument();
- Element root = doc.getDefaultRootElement();
- return root.getElementIndex(textComponent.getCaretPosition());
- }
-
-
- /**
- * Returns the link redirector, if any.
- *
- * @return The link redirector, or null
if none.
- * @see #setLinkRedirector(LinkRedirector)
- */
- public static LinkRedirector getLinkRedirector() {
- return linkRedirector;
- }
-
-
- /**
- * Returns the default list cell renderer used when a completion provider
- * does not supply its own.
- *
- * @return The default list cell renderer.
- * @see #setListCellRenderer(ListCellRenderer)
- */
- public ListCellRenderer getListCellRenderer() {
- return renderer;
- }
-
-
- /**
- * Returns the renderer to use for {@link Completion}s in the optional
- * parameter choices popup window (displayed when a
- * {@link ParameterizedCompletion} is code-completed). If this returns
- * null
, a default renderer is used.
- *
- * @return The renderer to use.
- * @see #setParamChoicesRenderer(ListCellRenderer)
- * @see #isParameterAssistanceEnabled()
- */
- public ListCellRenderer getParamChoicesRenderer() {
- return paramChoicesRenderer;
- }
-
-
- /**
- * Returns the text to replace with in the document. This is a
- * "last-chance" hook for subclasses to make special modifications to the
- * completion text inserted. The default implementation simply returns
- * c.getReplacementText(). You usually will not need to modify
- * this method.
- *
- * @param c The completion being inserted.
- * @param doc The document being modified.
- * @param start The start of the text being replaced.
- * @param len The length of the text being replaced.
- * @return The text to replace with.
- */
- protected String getReplacementText(Completion c, Document doc, int start,
- int len) {
- return c.getReplacementText();
- }
-
-
- /**
- * Returns whether the "description window" should be shown alongside
- * the completion window.
- *
- * @return Whether the description window should be shown.
- * @see #setShowDescWindow(boolean)
- */
- public boolean isShowDescWindow() {
- return showDescWindow;
- }
-
-
- /**
- * Returns the style context describing how auto-completion related
- * highlights in the editor are rendered.
- *
- * @return The style context.
- */
- public static AutoCompletionStyleContext getStyleContext() {
- return STYLE_CONTEXT;
- }
-
-
- /**
- * Returns the text component for which auto-completion is enabled.
- *
- * @return The text component, or null
if this
- * {@link AutoCompletion} is not installed on any text component.
- * @see #install(JTextComponent)
- */
- public JTextComponent getTextComponent() {
- return textComponent;
- }
-
-
- /**
- * Returns the orientation of the text component we're installed to.
- *
- * @return The orientation of the text component, or null
if
- * we are not installed on one.
- */
- ComponentOrientation getTextComponentOrientation() {
- return textComponent == null ? null :
- textComponent.getComponentOrientation();
- }
-
-
- /**
- * Returns the "trigger key" used for auto-complete.
- *
- * @return The trigger key.
- * @see #setTriggerKey(KeyStroke)
- */
- public KeyStroke getTriggerKey() {
- return trigger;
- }
-
-
- /**
- * Hides any child windows being displayed by the auto-completion system.
- *
- * @return Whether any windows were visible.
- */
- public boolean hideChildWindows() {
- //return hidePopupWindow() || hideToolTipWindow();
- boolean res = hidePopupWindow();
- res |= hideParameterCompletionPopups();
- return res;
- }
-
-
- /**
- * Hides and disposes of any parameter completion-related popups.
- *
- * @return Whether any such windows were visible (and thus hidden).
- */
- private boolean hideParameterCompletionPopups() {
- if (pcc != null) {
- pcc.deactivate();
- pcc = null;
- return true;
- }
- return false;
- }
-
-
- /**
- * Hides the popup window, if it is visible.
- *
- * @return Whether the popup window was visible.
- */
- private boolean hidePopupWindow() {
- if (popupWindow != null) {
- if (popupWindow.isVisible()) {
- popupWindow.setVisible(false);
- return true;
- }
- }
- return false;
- }
-
-
- /**
- * Determines whether debug should be enabled for the AutoCompletion
- * library. This method checks a system property, but takes care of
- * {@link SecurityException}s in case we're in an applet or WebStart.
- *
- * @return Whether debug should be enabled.
- */
- private static boolean initDebug() {
- boolean debug = false;
- try {
- debug = Boolean.getBoolean("AutoCompletion.debug");
- } catch (SecurityException se) { // We're in an applet or WebStart.
- debug = false;
- }
- return debug;
- }
-
-
- /**
- * Installs this auto-completion on a text component. If this
- * {@link AutoCompletion} is already installed on another text component,
- * it is uninstalled first.
- *
- * @param c The text component.
- * @see #uninstall()
- */
- public void install(JTextComponent c) {
-
- if (textComponent != null) {
- uninstall();
- }
-
- this.textComponent = c;
- installTriggerKey(getTriggerKey());
-
- // Install the function completion key, if there is one.
- // NOTE: We cannot do this if the start char is ' ' (e.g. just a space
- // between the function name and parameters) because it overrides
- // RSTA's special space action. It seems KeyStorke.getKeyStroke(' ')
- // hoses ctrl+space, shift+space, etc., even though I think it
- // shouldn't...
- char start = provider.getParameterListStart();
- if (start != 0 && start != ' ') {
- InputMap im = c.getInputMap();
- ActionMap am = c.getActionMap();
- KeyStroke ks = KeyStroke.getKeyStroke(start);
- oldParenKey = im.get(ks);
- im.put(ks, PARAM_COMPLETE_KEY);
- oldParenAction = am.get(PARAM_COMPLETE_KEY);
- am.put(PARAM_COMPLETE_KEY,
- new ParameterizedCompletionStartAction(start));
- }
-
- textComponentListener.addTo(this.textComponent);
- // In case textComponent is already in a window...
- textComponentListener.hierarchyChanged(null);
-
- if (isAutoActivationEnabled()) {
- autoActivationListener.addTo(this.textComponent);
- }
-
- UIManager.addPropertyChangeListener(lafListener);
- updateUI(); // In case there have been changes since we uninstalled
-
- }
-
-
- /**
- * Installs a "trigger key" action onto the current text component.
- *
- * @param ks The keystroke that should trigger the action.
- */
- private void installTriggerKey(KeyStroke ks) {
- InputMap im = textComponent.getInputMap();
- oldTriggerKey = im.get(ks);
- im.put(ks, PARAM_TRIGGER_KEY);
- ActionMap am = textComponent.getActionMap();
- oldTriggerAction = am.get(PARAM_TRIGGER_KEY);
- am.put(PARAM_TRIGGER_KEY, new AutoCompleteAction());
- }
-
-
- /**
- * Returns whether auto-activation is enabled (that is, whether the
- * completion popup will automatically appear after a delay when the user
- * types an appropriate character). Note that this parameter will be
- * ignored if auto-completion is disabled.
- *
- * @return Whether auto-activation is enabled.
- * @see #setAutoActivationEnabled(boolean)
- * @see #getAutoActivationDelay()
- * @see #isAutoCompleteEnabled()
- */
- public boolean isAutoActivationEnabled() {
- return autoActivationEnabled;
- }
-
-
- /**
- * Returns whether auto-completion is enabled.
- *
- * @return Whether auto-completion is enabled.
- * @see #setAutoCompleteEnabled(boolean)
- */
- public boolean isAutoCompleteEnabled() {
- return autoCompleteEnabled;
- }
-
-
- /**
- * Returns whether parameter assistance is enabled.
- *
- * @return Whether parameter assistance is enabled.
- * @see #setParameterAssistanceEnabled(boolean)
- */
- public boolean isParameterAssistanceEnabled() {
- return parameterAssistanceEnabled;
- }
-
-
- /**
- * Returns whether the completion popup window is visible.
- *
- * @return Whether the completion popup window is visible.
- */
- public boolean isPopupVisible() {
- return popupWindow != null && popupWindow.isVisible();
- }
-
- private boolean needSetPopupWindow(int count, int textLen) {
- return (count == 1 && (isPopupVisible() || textLen == 0))
- || (count == 1 && !isAutoCompleteSingleChoices())
- || count > 1;
- }
-
- private FormulaAutoCompletePopupWindow createAutoCompletePopupWindow() {
- FormulaAutoCompletePopupWindow popupWindow = new FormulaAutoCompletePopupWindow(parentWindow, this);
+ @Override
+ protected AutoCompleteWithExtraRefreshPopupWindow createAutoCompletePopupWindow() {
+ FormulaAutoCompletePopupWindow popupWindow = new FormulaAutoCompletePopupWindow(getParentWindow(), this);
// Completion is usually done for code, which is always done
// LTR, so make completion stuff RTL only if text component is
// also RTL.
popupWindow.applyComponentOrientation(
getTextComponentOrientation());
- if (renderer != null) {
- popupWindow.setListCellRenderer(renderer);
+ if (getCellRender() != null) {
+ popupWindow.setListCellRenderer(getCellRender());
}
- if (preferredChoicesWindowSize != null) {
- popupWindow.setSize(preferredChoicesWindowSize);
+ if (getPreferredChoicesWindowSize() != null) {
+ popupWindow.setSize(getPreferredChoicesWindowSize());
}
- if (preferredDescWindowSize != null) {
+ if (getPreferredDescWindowSize() != null) {
popupWindow.setDescriptionWindowSize(
- preferredDescWindowSize);
+ getPreferredDescWindowSize());
}
return popupWindow;
}
- /**
- * Sets the delay between when the user types a character and when the
- * code completion popup should automatically appear (if applicable).
- *
- * @param ms The delay, in milliseconds. This should be greater than zero.
- * @see #getAutoActivationDelay()
- */
- public void setAutoActivationDelay(int ms) {
- ms = Math.max(0, ms);
- autoActivationListener.timer.stop();
- autoActivationListener.timer.setInitialDelay(ms);
- }
-
-
- /**
- * Toggles whether auto-activation is enabled. Note that auto-activation
- * also depends on auto-completion itself being enabled.
- *
- * @param enabled Whether auto-activation is enabled.
- * @see #isAutoActivationEnabled()
- * @see #setAutoActivationDelay(int)
- */
- public void setAutoActivationEnabled(boolean enabled) {
- if (enabled != autoActivationEnabled) {
- autoActivationEnabled = enabled;
- if (textComponent != null) {
- if (autoActivationEnabled) {
- autoActivationListener.addTo(textComponent);
- } else {
- autoActivationListener.removeFrom(textComponent);
- }
- }
- }
- }
-
-
- /**
- * Sets whether auto-completion is enabled.
- *
- * @param enabled Whether auto-completion is enabled.
- * @see #isAutoCompleteEnabled()
- */
- public void setAutoCompleteEnabled(boolean enabled) {
- if (enabled != autoCompleteEnabled) {
- autoCompleteEnabled = enabled;
- hidePopupWindow();
- }
- }
-
-
- /**
- * Sets whether, if a single auto-complete choice is available, it should
- * be automatically inserted, without displaying the popup menu.
- *
- * @param autoComplete Whether to auto-complete single choices.
- * @see #isAutoCompleteSingleChoices()
- */
- public void setAutoCompleteSingleChoices(boolean autoComplete) {
- autoCompleteSingleChoices = autoComplete;
- }
-
-
- /**
- * Sets the completion provider being used.
- *
- * @param provider The new completion provider. This cannot be
- * null
.
- * @throws IllegalArgumentException If provider
is
- * null
.
- */
- public void setCompletionProvider(CompletionProvider provider) {
- if (provider == null) {
- throw new IllegalArgumentException("provider cannot be null");
- }
- this.provider = provider;
- hidePopupWindow(); // In case new choices should be displayed.
- }
-
-
- /**
- * Sets the size of the completion choices window.
- *
- * @param w The new width.
- * @param h The new height.
- * @see #setDescriptionWindowSize(int, int)
- */
- public void setChoicesWindowSize(int w, int h) {
- preferredChoicesWindowSize = new Dimension(w, h);
- if (popupWindow != null) {
- popupWindow.setSize(preferredChoicesWindowSize);
- }
- }
-
-
- /**
- * Sets the size of the description window.
- *
- * @param w The new width.
- * @param h The new height.
- * @see #setChoicesWindowSize(int, int)
- */
- public void setDescriptionWindowSize(int w, int h) {
- preferredDescWindowSize = new Dimension(w, h);
- if (popupWindow != null) {
- popupWindow.setDescriptionWindowSize(preferredDescWindowSize);
- }
- }
-
-
- /**
- * Sets the handler to use when an external URL is clicked in the
- * description window. This handler can perform some action, such as
- * open the URL in a web browser. The default implementation will open
- * the URL in a browser, but only if running in Java 6. If you want
- * browser support for Java 5 and below, or otherwise want to respond to
- * hyperlink clicks, you will have to install your own handler to do so.
- *
- * @param handler The new handler.
- * @see #getExternalURLHandler()
- */
- public void setExternalURLHandler(ExternalURLHandler handler) {
- this.externalURLHandler = handler;
- }
-
-
- /**
- * Sets the redirector for external URL's found in code completion
- * documentation. When a non-local link in completion popups is clicked,
- * this redirector is given the chance to modify the URL fetched and
- * displayed.
- *
- * @param redirector The link redirector, or null
for
- * none.
- * @see #getLinkRedirector()
- */
- public static void setLinkRedirector(LinkRedirector redirector) {
- linkRedirector = redirector;
- }
-
-
- /**
- * Sets the default list cell renderer to use when a completion provider
- * does not supply its own.
- *
- * @param renderer The renderer to use. If this is null
,
- * a default renderer is used.
- * @see #getListCellRenderer()
- */
- public void setListCellRenderer(ListCellRenderer renderer) {
- this.renderer = renderer;
- if (popupWindow != null) {
- popupWindow.setListCellRenderer(renderer);
- hidePopupWindow();
- }
- }
-
-
- /**
- * Sets the renderer to use for {@link Completion}s in the optional
- * parameter choices popup window (displayed when a
- * {@link ParameterizedCompletion} is code-completed). If this isn't set,
- * a default renderer is used.
- *
- * @param r The renderer to use.
- * @see #getParamChoicesRenderer()
- * @see #setParameterAssistanceEnabled(boolean)
- */
- public void setParamChoicesRenderer(ListCellRenderer r) {
- paramChoicesRenderer = r;
- }
-
-
- /**
- * Sets whether parameter assistance is enabled. If parameter assistance
- * is enabled, and a "parameterized" completion (such as a function or
- * method) is inserted, the user will get "assistance" in inserting the
- * parameters in the form of a popup window with documentation and easy
- * tabbing through the arguments (as seen in Eclipse and NetBeans).
- *
- * @param enabled Whether parameter assistance should be enabled.
- * @see #isParameterAssistanceEnabled()
- */
- public void setParameterAssistanceEnabled(boolean enabled) {
- parameterAssistanceEnabled = enabled;
- }
-
-
- /**
- * Sets whether the "description window" should be shown beside the
- * completion window.
- *
- * @param show Whether to show the description window.
- * @see #isShowDescWindow()
- */
- public void setShowDescWindow(boolean show) {
- hidePopupWindow(); // Needed to force it to take effect
- showDescWindow = show;
- }
-
-
- /**
- * Sets the keystroke that should be used to trigger the auto-complete
- * popup window.
- *
- * @param ks The keystroke.
- * @throws IllegalArgumentException If ks
is null
.
- * @see #getTriggerKey()
- */
- public void setTriggerKey(KeyStroke ks) {
- if (ks == null) {
- throw new IllegalArgumentException("trigger key cannot be null");
- }
- if (!ks.equals(trigger)) {
- if (textComponent != null) {
- // Put old trigger action back.
- uninstallTriggerKey();
- // Grab current action for new trigger and replace it.
- installTriggerKey(ks);
- }
- trigger = ks;
- }
- }
-
-
- /**
- * Uninstalls this auto-completion from its text component. If it is not
- * installed on any text component, nothing happens.
- *
- * @see #install(JTextComponent)
- */
- public void uninstall() {
-
- if (textComponent != null) {
-
- hidePopupWindow(); // Unregisters listeners, actions, etc.
-
- uninstallTriggerKey();
-
- // Uninstall the function completion key.
- char start = provider.getParameterListStart();
- if (start != 0) {
- KeyStroke ks = KeyStroke.getKeyStroke(start);
- InputMap im = textComponent.getInputMap();
- im.put(ks, oldParenKey);
- ActionMap am = textComponent.getActionMap();
- am.put(PARAM_COMPLETE_KEY, oldParenAction);
- }
-
- textComponentListener.removeFrom(textComponent);
- if (parentWindow != null) {
- parentWindowListener.removeFrom(parentWindow);
- }
-
- if (isAutoActivationEnabled()) {
- autoActivationListener.removeFrom(textComponent);
- }
-
- UIManager.removePropertyChangeListener(lafListener);
-
- textComponent = null;
- popupWindow = null;
-
- }
-
- }
-
-
- /**
- * Replaces the "trigger key" action with the one that was there
- * before auto-completion was installed.
- */
- private void uninstallTriggerKey() {
- InputMap im = textComponent.getInputMap();
- im.put(trigger, oldTriggerKey);
- ActionMap am = textComponent.getActionMap();
- am.put(PARAM_TRIGGER_KEY, oldTriggerAction);
- }
-
-
- /**
- * Updates the LookAndFeel of the popup window. Applications can call
- * this method as appropriate if they support changing the LookAndFeel
- * at runtime.
- */
- private void updateUI() {
- if (popupWindow != null) {
- popupWindow.updateUI();
- }
- if (pcc != null) {
- pcc.updateUI();
- }
- // Will practically always be a JComponent (a JLabel)
- if (paramChoicesRenderer instanceof JComponent) {
- ((JComponent) paramChoicesRenderer).updateUI();
- }
- }
-
-
- /**
- * Listens for events in the text component to auto-activate the code
- * completion popup.
- */
- private class AutoActivationListener extends FocusAdapter
- implements DocumentListener, CaretListener, ActionListener {
-
- private Timer timer;
- private boolean justInserted;
-
- public AutoActivationListener() {
- timer = new Timer(200, this);
- timer.setRepeats(false);
- }
-
- public void actionPerformed(ActionEvent e) {
- doCompletion();
- }
-
- public void addTo(JTextComponent tc) {
- tc.addFocusListener(this);
- tc.getDocument().addDocumentListener(this);
- tc.addCaretListener(this);
- }
-
- public void caretUpdate(CaretEvent e) {
- if (justInserted) {
- justInserted = false;
- } else {
- timer.stop();
- }
- }
-
- public void changedUpdate(DocumentEvent e) {
- // Ignore
- }
-
- @Override
- public void focusLost(FocusEvent e) {
- timer.stop();
- //hideChildWindows(); Other listener will do this
- }
-
- public void insertUpdate(DocumentEvent e) {
- justInserted = false;
- if (isAutoCompleteEnabled() && isAutoActivationEnabled() &&
- e.getLength() == 1) {
- if (provider.isAutoActivateOkay(textComponent)) {
- timer.restart();
- justInserted = true;
- } else {
- timer.stop();
- }
- } else {
- timer.stop();
- }
- }
-
- public void removeFrom(JTextComponent tc) {
- tc.removeFocusListener(this);
- tc.getDocument().removeDocumentListener(this);
- tc.removeCaretListener(this);
- timer.stop();
- justInserted = false;
- }
-
- public void removeUpdate(DocumentEvent e) {
- timer.stop();
- }
-
- }
-
-
- /**
- * The Action
that displays the popup window if
- * auto-completion is enabled.
- */
- private class AutoCompleteAction extends AbstractAction {
-
- public void actionPerformed(ActionEvent e) {
- if (isAutoCompleteEnabled()) {
- refreshPopupWindow();
- } else if (oldTriggerAction != null) {
- oldTriggerAction.actionPerformed(e);
- }
- }
-
- }
-
-
- /**
- * Listens for LookAndFeel changes and updates the various popup windows
- * involved in auto-completion accordingly.
- */
- private class LookAndFeelChangeListener implements PropertyChangeListener {
-
- public void propertyChange(PropertyChangeEvent e) {
- String name = e.getPropertyName();
- if ("lookAndFeel".equals(name)) {
- updateUI();
- }
- }
-
- }
-
-
- /**
- * Action that starts a parameterized completion, e.g. after '(' is
- * typed.
- */
- private class ParameterizedCompletionStartAction extends AbstractAction {
-
- private String start;
-
- public ParameterizedCompletionStartAction(char ch) {
- this.start = Character.toString(ch);
- }
-
- public void actionPerformed(ActionEvent e) {
-
- // Prevents keystrokes from messing up
- boolean wasVisible = hidePopupWindow();
-
- // Only proceed if they were selecting a completion
- if (!wasVisible || !isParameterAssistanceEnabled()) {
- textComponent.replaceSelection(start);
- return;
- }
-
- Completion c = popupWindow.getSelection();
- if (c instanceof ParameterizedCompletion) { // Should always be true
- // Fixes capitalization of the entered text.
- insertCompletion(c, true);
- }
-
- }
-
- }
-
-
- /**
- * Listens for events in the parent window of the text component with
- * auto-completion enabled.
- */
- private class ParentWindowListener extends ComponentAdapter
- implements WindowFocusListener {
-
- public void addTo(Window w) {
- w.addComponentListener(this);
- w.addWindowFocusListener(this);
- }
-
- @Override
- public void componentHidden(ComponentEvent e) {
- hideChildWindows();
- }
-
- @Override
- public void componentMoved(ComponentEvent e) {
- hideChildWindows();
- }
-
- @Override
- public void componentResized(ComponentEvent e) {
- hideChildWindows();
- }
-
- public void removeFrom(Window w) {
- w.removeComponentListener(this);
- w.removeWindowFocusListener(this);
- }
-
- public void windowGainedFocus(WindowEvent e) {
- }
-
- public void windowLostFocus(WindowEvent e) {
- hideChildWindows();
- }
-
- }
-
-
- /**
- * Listens for events from the text component we're installed on.
- */
- private class TextComponentListener extends FocusAdapter
- implements HierarchyListener {
-
- void addTo(JTextComponent tc) {
- tc.addFocusListener(this);
- tc.addHierarchyListener(this);
- }
-
- /**
- * Hide the auto-completion windows when the text component loses
- * focus.
- */
- @Override
- public void focusLost(FocusEvent e) {
- hideChildWindows();
- }
-
- /**
- * Called when the component hierarchy for our text component changes.
- * When the text component is added to a new {@link Window}, this
- * method registers listeners on that Window
.
- *
- * @param e The event.
- */
- public void hierarchyChanged(HierarchyEvent e) {
-
- // NOTE: e many be null as we call this method at other times.
- //System.out.println("Hierarchy changed! " + e);
-
- Window oldParentWindow = parentWindow;
- parentWindow = SwingUtilities.getWindowAncestor(textComponent);
- if (parentWindow != oldParentWindow) {
- if (oldParentWindow != null) {
- parentWindowListener.removeFrom(oldParentWindow);
- }
- if (parentWindow != null) {
- parentWindowListener.addTo(parentWindow);
- }
- }
-
- }
-
- public void removeFrom(JTextComponent tc) {
- tc.removeFocusListener(this);
- tc.removeHierarchyListener(this);
- }
-
- }
-
- public void installVariableTree(FormulaPane.VariableTreeAndDescriptionArea jComp) {
- area = jComp;
- }
-
/**
* Inserts a completion. Any time a code completion event occurs, the
* actual text insertion happens through this method.
@@ -1223,89 +87,4 @@ public class FormulaPaneAutoCompletion extends AutoCompletion {
}
}
-
- /**
- * Displays a "tool tip" detailing the inputs to the function just entered.
- *
- * @param pc The completion.
- * @param typedParamListStartChar Whether the parameterized completion list
- * starting character was typed.
- */
- private void startParameterizedCompletionAssistance(
- ParameterizedCompletion pc, boolean typedParamListStartChar) {
-
- // Get rid of the previous tool tip window, if there is one.
- hideParameterCompletionPopups();
-
- // Don't bother with a tool tip if there are no parameters, but if
- // they typed e.g. the opening '(', make them overtype the ')'.
- if (pc.getParamCount() == 0 && !(pc instanceof TemplateCompletion)) {
- CompletionProvider p = pc.getProvider();
- char end = p.getParameterListEnd(); // Might be '\0'
- String text = end == '\0' ? "" : Character.toString(end);
- if (typedParamListStartChar) {
- String template = "${}" + text + "${cursor}";
- textComponent.replaceSelection(Character.toString(p.getParameterListStart()));
- TemplateCompletion tc = new TemplateCompletion(p, null, null, template);
- pc = tc;
- } else {
- text = p.getParameterListStart() + text;
- textComponent.replaceSelection(text);
- return;
- }
- }
-
- pcc = new ParameterizedCompletionContext(parentWindow, this, pc);
- pcc.activate();
-
- }
-
- protected int refreshPopupWindow() {
- // A return value of null => don't suggest completions
- String text = provider.getAlreadyEnteredText(textComponent);
- if (text == null && !isPopupVisible()) {
- return getLineOfCaret();
- }
- // If the popup is currently visible, and they type a space (or any
- // character that resets the completion list to "all completions"),
- // the popup window should be hidden instead of being reset to show
- // everything.
- int textLen = text == null ? 0 : text.length();
- if (textLen == 0 && isPopupVisible()) {
- hidePopupWindow();
- return getLineOfCaret();
- }
- final List completions = provider.getCompletions(textComponent);
- int count = completions.size();
- if (needSetPopupWindow(count, textLen)) {
- if (popupWindow == null) {
- popupWindow = createAutoCompletePopupWindow();
- }
- popupWindow.installComp(area);
- popupWindow.setCompletions(completions);
- if (!popupWindow.isVisible()) {
- Rectangle r = null;
- try {
- r = textComponent.modelToView(textComponent.getCaretPosition());
- } catch (BadLocationException ble) {
- return -1;
- }
- Point p = new Point(r.x, r.y);
- SwingUtilities.convertPointToScreen(p, textComponent);
- r.x = p.x;
- r.y = p.y;
- popupWindow.setLocationRelativeTo(r);
- popupWindow.setVisible(true);
- }
- } else if (count == 1) { // !isPopupVisible && autoCompleteSingleChoices
- SwingUtilities.invokeLater(new Runnable() {
- public void run() {
- insertCompletion(completions.get(0));
- }
- });
- } else {
- hidePopupWindow();
- }
- return getLineOfCaret();
- }
}
diff --git a/designer-base/src/main/java/com/fr/design/gui/autocomplete/JSAutoCompletePopupWindow.java b/designer-base/src/main/java/com/fr/design/gui/autocomplete/JSAutoCompletePopupWindow.java
new file mode 100644
index 000000000..80a26bf96
--- /dev/null
+++ b/designer-base/src/main/java/com/fr/design/gui/autocomplete/JSAutoCompletePopupWindow.java
@@ -0,0 +1,16 @@
+package com.fr.design.gui.autocomplete;
+
+import java.awt.Window;
+
+public class JSAutoCompletePopupWindow extends AutoCompleteWithExtraRefreshPopupWindow {
+
+ /**
+ * Constructor.
+ *
+ * @param parent The parent window (hosting the text component).
+ * @param ac The auto-completion instance.
+ */
+ public JSAutoCompletePopupWindow(Window parent, AutoCompletion ac) {
+ super(parent, ac);
+ }
+}
diff --git a/designer-base/src/main/java/com/fr/design/gui/autocomplete/JSImplPaneAutoCompletion.java b/designer-base/src/main/java/com/fr/design/gui/autocomplete/JSImplPaneAutoCompletion.java
new file mode 100644
index 000000000..0c534bf72
--- /dev/null
+++ b/designer-base/src/main/java/com/fr/design/gui/autocomplete/JSImplPaneAutoCompletion.java
@@ -0,0 +1,31 @@
+package com.fr.design.gui.autocomplete;
+
+public class JSImplPaneAutoCompletion extends AutoCompletionWithExtraRefresh{
+ /**
+ * Constructor.
+ *
+ * @param provider The completion provider. This cannot be
+ * null
.
+ */
+ public JSImplPaneAutoCompletion(CompletionProvider provider) {
+ super(provider);
+ }
+
+ @Override
+ protected AutoCompleteWithExtraRefreshPopupWindow createAutoCompletePopupWindow() {
+ JSAutoCompletePopupWindow popupWindow = new JSAutoCompletePopupWindow(getParentWindow(),this);
+ popupWindow.applyComponentOrientation(
+ getTextComponentOrientation());
+ if (getCellRender() != null) {
+ popupWindow.setListCellRenderer(getCellRender());
+ }
+ if (getPreferredChoicesWindowSize() != null) {
+ popupWindow.setSize(getPreferredChoicesWindowSize());
+ }
+ if (getPreferredDescWindowSize() != null) {
+ popupWindow.setDescriptionWindowSize(
+ getPreferredDescWindowSize());
+ }
+ return popupWindow;
+ }
+}
diff --git a/designer-base/src/main/java/com/fr/design/javascript/JSContentPane.java b/designer-base/src/main/java/com/fr/design/javascript/JSContentPane.java
index 10e11c8fb..10bee69d2 100644
--- a/designer-base/src/main/java/com/fr/design/javascript/JSContentPane.java
+++ b/designer-base/src/main/java/com/fr/design/javascript/JSContentPane.java
@@ -4,7 +4,9 @@ import com.fr.design.DesignerEnvManager;
import com.fr.design.border.UIRoundedBorder;
import com.fr.design.constants.KeyWords;
import com.fr.design.constants.UIConstants;
+import com.fr.design.dialog.BasicDialog;
import com.fr.design.dialog.BasicPane;
+import com.fr.design.dialog.DialogActionAdapter;
import com.fr.design.gui.autocomplete.AutoCompletion;
import com.fr.design.gui.autocomplete.BasicCompletion;
import com.fr.design.gui.autocomplete.CompletionProvider;
@@ -15,31 +17,145 @@ import com.fr.design.gui.ilable.UILabel;
import com.fr.design.gui.syntax.ui.rsyntaxtextarea.RSyntaxTextArea;
import com.fr.design.gui.syntax.ui.rsyntaxtextarea.SyntaxConstants;
import com.fr.design.javascript.beautify.JavaScriptFormatHelper;
+import com.fr.design.javascript.jsapi.JSImplPopulateAction;
+import com.fr.design.javascript.jsapi.JSImplUpdateAction;
import com.fr.design.layout.FRGUIPaneFactory;
+import com.fr.design.mainframe.DesignerContext;
import com.fr.general.IOUtils;
+import com.fr.js.JavaScriptImpl;
-import javax.swing.*;
-import java.awt.*;
+import java.awt.BorderLayout;
+import java.awt.Cursor;
+import java.awt.Dimension;
+import java.awt.FontMetrics;
import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.ArrayList;
+import javax.swing.JPanel;
+import javax.swing.KeyStroke;
+import javax.swing.SwingConstants;
+import javax.swing.SwingWorker;
public class JSContentPane extends BasicPane {
- private RSyntaxTextArea contentTextArea;
- private UILabel funNameLabel;
+ protected RSyntaxTextArea contentTextArea;
+ private UILabel funNameLabel = new UILabel();
private AutoCompletion ac;
private static final Dimension FUNCTION_NAME_LABEL_SIZE = new Dimension(300, 80);
-
+ private String[] defaultArgs;
private int titleWidth = 180;
+ private JPanel labelPane = new JPanel(new BorderLayout(6, 4));;
+ private NewJavaScriptImplPane newJavaScriptImplPane = null;
+ private JavaScriptImpl javaScript;
+ private JSImplUpdateAction jsImplUpdateAction;
+ private JSImplPopulateAction jsImplPopulateAction;
+ private boolean modal;
+ BasicDialog advancedEditorDialog ;
+ public JSContentPane(){}
public JSContentPane(String[] args) {
+ defaultArgs = args;
this.setLayout(FRGUIPaneFactory.createBorderLayout());
- funNameLabel = new UILabel();
- this.setFunctionTitle(args);
+ initFunctionTitle(args);
+
+ JPanel jsParaPane = createJSParaPane();
+ addNewPaneLabel();
+ this.add(jsParaPane, BorderLayout.NORTH);
+
+ UIScrollPane sp = createContentTextAreaPanel();
+ initContentTextAreaListener();
+ this.add(sp, BorderLayout.CENTER);
+
+ UILabel funNameLabel2 = new UILabel();
+ funNameLabel2.setText("}");
+ this.add(funNameLabel2, BorderLayout.SOUTH);
+ }
+ public JSContentPane(String[] args,boolean modal) {
+ this(args);
+ this.modal = modal;
+ }
+
+
+ public void setJsImplUpdateAction(JSImplUpdateAction jsImplUpdateAction){
+ this.jsImplUpdateAction = jsImplUpdateAction;
+ }
+
+ public void setJsImplPopulateAction(JSImplPopulateAction jsImplPopulateAction){
+ this.jsImplPopulateAction = jsImplPopulateAction;
+ }
+
+ public void updateJSImpl(JavaScriptImpl javaScript){
+ this.javaScript = javaScript;
+ }
+
+
+ private void addNewPaneLabel(){
+ UILabel advancedEditorLabel = new UILabel(com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Advanced_Editor"), SwingConstants.LEFT);
+ advancedEditorLabel.setCursor(new Cursor(Cursor.HAND_CURSOR));
+ advancedEditorLabel.addMouseListener(new MouseAdapter() {
+ @Override
+ public void mouseClicked(MouseEvent e) {
+ if(newJavaScriptImplPane == null){
+ newJavaScriptImplPane = new NewJavaScriptImplPane(defaultArgs);
+ }
+ jsImplUpdateAction.update(javaScript);
+ newJavaScriptImplPane.populate(javaScript);
+ if(advancedEditorDialog == null || !advancedEditorDialog.isVisible()) {
+ advancedEditorDialog = newJavaScriptImplPane.showWindow(DesignerContext.getDesignerFrame(), new DialogActionAdapter() {
+ @Override
+ public void doOk() {
+ if (javaScript != null) {
+ newJavaScriptImplPane.updateBean(javaScript);
+ jsImplPopulateAction.populate(javaScript);
+ }
+ }
+
+ @Override
+ public void doCancel() {
+ super.doCancel();
+ }
+ });
+ advancedEditorDialog.setModal(modal);
+ advancedEditorDialog.setResizable(true);
+ advancedEditorDialog.pack();
+ advancedEditorDialog.setVisible(true);
+ }
+ advancedEditorDialog.requestFocus();
+ }
+ });
+ labelPane.add(advancedEditorLabel,BorderLayout.CENTER);
+ }
+
+ protected UIScrollPane createContentTextAreaPanel(){
+ contentTextArea = new RSyntaxTextArea();
+ contentTextArea.setCloseCurlyBraces(true);
+ contentTextArea.setLineWrap(true);
+ contentTextArea.setSyntaxEditingStyle(SyntaxConstants.SYNTAX_STYLE_JAVASCRIPT);
+ contentTextArea.setCodeFoldingEnabled(true);
+ contentTextArea.setAntiAliasingEnabled(true);
+ return new UIScrollPane(contentTextArea);
+ }
+
+ private void initContentTextAreaListener(){
+ contentTextArea.addFocusListener(new FocusListener() {
+ @Override
+ public void focusGained(FocusEvent e) {
+ // 获得焦点时 安装
+ installAutoCompletion();
+ }
+
+ @Override
+ public void focusLost(FocusEvent e) {
+ // 失去焦点时 卸载
+ uninstallAutoCompletion();
+ }
+ });
+ }
+
+ protected JPanel createJSParaPane(){
UILabel label = new UILabel(com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Basic_Format_JavaScript"), IOUtils.readIcon("com/fr/design/images/edit/format.png"), SwingConstants.LEFT);
label.setCursor(new Cursor(Cursor.HAND_CURSOR));
label.setToolTipText(com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Basic_Format_JavaScript"));
@@ -65,43 +181,20 @@ public class JSContentPane extends BasicPane {
}
});
- //REPORT-10533 用户参数多达25个,导致JS没地方写,增加滚动条显示
+ labelPane.add(label,BorderLayout.EAST);
JPanel jsParaPane = new JPanel(new BorderLayout(4, 4));
jsParaPane.setPreferredSize(new Dimension(300, 80));
UIScrollPane scrollPane = new UIScrollPane(funNameLabel);
scrollPane.setPreferredSize(FUNCTION_NAME_LABEL_SIZE);
scrollPane.setBorder(new UIRoundedBorder(UIConstants.TITLED_BORDER_COLOR, 1, UIConstants.ARC));
jsParaPane.add(scrollPane, BorderLayout.WEST);
- jsParaPane.add(label, BorderLayout.EAST);
- this.add(jsParaPane, BorderLayout.NORTH);
-
- contentTextArea = new RSyntaxTextArea();
- contentTextArea.setCloseCurlyBraces(true);
- contentTextArea.setLineWrap(true);
- contentTextArea.setSyntaxEditingStyle(SyntaxConstants.SYNTAX_STYLE_JAVASCRIPT);
- contentTextArea.setCodeFoldingEnabled(true);
- contentTextArea.setAntiAliasingEnabled(true);
-
- UIScrollPane sp = new UIScrollPane(contentTextArea);
- this.add(sp, BorderLayout.CENTER);
-
- contentTextArea.addFocusListener(new FocusListener() {
- @Override
- public void focusGained(FocusEvent e) {
- // 获得焦点时 安装
- installAutoCompletion();
- }
-
- @Override
- public void focusLost(FocusEvent e) {
- // 失去焦点时 卸载
- uninstallAutoCompletion();
- }
- });
+ jsParaPane.add(labelPane, BorderLayout.EAST);
+ return jsParaPane;
+ }
- UILabel funNameLabel2 = new UILabel();
- funNameLabel2.setText("}");
- this.add(funNameLabel2, BorderLayout.SOUTH);
+ protected void initFunctionTitle(String[] args){
+ funNameLabel = new UILabel();
+ this.setFunctionTitle(args);
}
private KeyStroke convert2KeyStroke(String ks) {
diff --git a/designer-base/src/main/java/com/fr/design/javascript/JSContentWithDescriptionPane.java b/designer-base/src/main/java/com/fr/design/javascript/JSContentWithDescriptionPane.java
new file mode 100644
index 000000000..b1516fe36
--- /dev/null
+++ b/designer-base/src/main/java/com/fr/design/javascript/JSContentWithDescriptionPane.java
@@ -0,0 +1,858 @@
+package com.fr.design.javascript;
+
+import com.fr.design.border.UIRoundedBorder;
+import com.fr.design.constants.UIConstants;
+import com.fr.design.gui.autocomplete.AutoCompleteExtraRefreshComponent;
+import com.fr.design.gui.autocomplete.BasicCompletion;
+import com.fr.design.gui.autocomplete.CompletionCellRenderer;
+import com.fr.design.gui.autocomplete.CompletionProvider;
+import com.fr.design.gui.autocomplete.DefaultCompletionProvider;
+import com.fr.design.gui.autocomplete.JSImplPaneAutoCompletion;
+import com.fr.design.gui.ibutton.UIButton;
+import com.fr.design.gui.icontainer.UIScrollPane;
+import com.fr.design.gui.ilable.UILabel;
+import com.fr.design.gui.itextarea.UITextArea;
+import com.fr.design.gui.itextfield.UITextField;
+import com.fr.design.i18n.Toolkit;
+import com.fr.design.javascript.jsapi.JSAPITreeHelper;
+import com.fr.design.javascript.jsapi.JSAPIUserObject;
+import com.fr.design.layout.FRGUIPaneFactory;
+import com.fr.general.CloudCenter;
+import com.fr.general.ComparatorUtils;
+import com.fr.general.http.HttpToolbox;
+import com.fr.json.JSONArray;
+import com.fr.json.JSONException;
+import com.fr.json.JSONObject;
+import com.fr.log.FineLoggerFactory;
+import com.fr.stable.StringUtils;
+import java.awt.BorderLayout;
+import java.awt.Color;
+import java.awt.Component;
+import java.awt.Desktop;
+import java.awt.Dimension;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.FocusAdapter;
+import java.awt.event.FocusEvent;
+import java.awt.event.KeyAdapter;
+import java.awt.event.KeyEvent;
+import java.awt.event.KeyListener;
+import java.awt.event.MouseAdapter;
+import java.awt.event.MouseEvent;
+import java.awt.event.MouseListener;
+import java.io.IOException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+import javax.swing.BorderFactory;
+import javax.swing.DefaultListCellRenderer;
+import javax.swing.DefaultListModel;
+import javax.swing.JComponent;
+import javax.swing.JList;
+import javax.swing.JPanel;
+import javax.swing.JPopupMenu;
+import javax.swing.JScrollPane;
+import javax.swing.JTree;
+import javax.swing.event.ListSelectionEvent;
+import javax.swing.event.ListSelectionListener;
+import javax.swing.event.TreeSelectionEvent;
+import javax.swing.event.TreeSelectionListener;
+import javax.swing.tree.DefaultMutableTreeNode;
+import javax.swing.tree.DefaultTreeCellRenderer;
+import javax.swing.tree.DefaultTreeModel;
+import javax.swing.tree.TreeNode;
+import javax.swing.tree.TreePath;
+
+public class JSContentWithDescriptionPane extends JSContentPane implements KeyListener {
+
+ //搜索关键词输入框
+ private UITextField keyWordTextField = new UITextField(16);
+ //搜索出的提示列表
+ private JList tipsList;
+ private DefaultListModel tipsListModel = new DefaultListModel();
+
+ private JList interfaceNameList;
+ private DefaultListModel interfaceNameModel;
+
+ private JTree moduleTree;
+
+ private DefaultCompletionProvider completionProvider;
+ private JSImplPaneAutoCompletion autoCompletion;
+
+ //函数说明文本框
+ private UITextArea descriptionTextArea;
+
+ private JPopupMenu popupMenu;
+
+ private InterfaceAndDescriptionPanel interfaceAndDescriptionPanel;
+ private JList helpDOCList;
+
+ private int ifHasBeenWriten = 0;
+ private int currentPosition = 0;
+ private int beginPosition = 0;
+ private int insertPosition = 0;
+ private static final String SEPARATOR = "_";
+
+ private static final int KEY_10 = 10;
+ //上下左右
+ private static final int KEY_37 = 37;
+ private static final int KEY_38 = 38;
+ private static final int KEY_39 = 39;
+ private static final int KEY_40 = 40;
+
+ private static final String URL_FOR_TEST_NETWORK = "https://www.baidu.com";
+
+ private static final String DOCUMENT_SEARCH_URL = CloudCenter.getInstance().acquireUrlByKind("af.doc_search");
+
+ private String currentValue;
+
+ public JSContentWithDescriptionPane(String[] args) {
+ this.setLayout(new BorderLayout());
+ //===============================
+ this.initFunctionTitle(args);
+ JPanel jsParaAndSearchPane = new JPanel(new BorderLayout());
+
+ //js函数声明面板
+ JPanel jsParaPane = createJSParaPane();
+
+ jsParaPane.setPreferredSize(new Dimension(650, 80));
+ //右上角的搜索提示面板
+ JPanel tipsPane = createTipsPane();
+
+ jsParaAndSearchPane.add(jsParaPane, BorderLayout.CENTER);
+ jsParaAndSearchPane.add(tipsPane, BorderLayout.EAST);
+
+ initPopTips();
+
+ //js文本编辑面板
+ UIScrollPane contentTextAreaPanel = createContentTextAreaPanel();
+ initContextAreaListener();
+
+ contentTextAreaPanel.setPreferredSize(new Dimension(850, 250));
+ //js函数结束标签
+ UILabel endBracketsLabel = new UILabel();
+ endBracketsLabel.setText("}");
+
+ //结尾括号和复用函数按钮面板
+ JPanel endBracketsPanel = FRGUIPaneFactory.createBorderLayout_S_Pane();
+ endBracketsPanel.add(endBracketsLabel, BorderLayout.WEST);
+
+ JPanel northPanel = FRGUIPaneFactory.createBorderLayout_S_Pane();
+ northPanel.add(jsParaAndSearchPane, BorderLayout.NORTH);
+
+ northPanel.add(contentTextAreaPanel, BorderLayout.CENTER);
+ northPanel.add(endBracketsPanel, BorderLayout.SOUTH);
+
+ //主编辑框,也就是面板的正中间部分
+ this.add(northPanel, BorderLayout.CENTER);
+
+ //函数分类和函数说明面板==================================
+ JPanel functionNameAndDescriptionPanel = createInterfaceAndDescriptionPanel();
+ functionNameAndDescriptionPanel.setPreferredSize(new Dimension(880, 220));
+
+ this.add(functionNameAndDescriptionPanel, BorderLayout.SOUTH);
+ }
+
+ private void initContextAreaListener() {
+ contentTextArea.addKeyListener(new KeyAdapter() {
+ @Override
+ public void keyTyped(KeyEvent e) {
+ if ((e.getKeyChar() >= 'A' && e.getKeyChar() <= 'z') || e.getKeyChar() == '_') {
+ if (autoCompletion != null) {
+ autoCompletion.doCompletion();
+ }
+ }
+ }
+
+ @Override
+ public void keyReleased(KeyEvent e) {
+ contentTextArea.setForeground(Color.black);
+ }
+ });
+ contentTextArea.addKeyListener(this);
+ contentTextArea.addFocusListener(new FocusAdapter() {
+ @Override
+ public void focusGained(FocusEvent e) {
+ if (autoCompletion == null) {
+ installAutoCompletion();
+ }
+ }
+
+ @Override
+ public void focusLost(FocusEvent e) {
+ uninstallAutoCompletion();
+ }
+ });
+ }
+
+ @Override
+ public void keyTyped(KeyEvent e) {
+
+ }
+
+ @Override
+ public void keyPressed(KeyEvent e) {
+ if (ifHasBeenWriten == 0) {
+ this.contentTextArea.setText(StringUtils.EMPTY);
+ }
+ }
+
+ @Override
+ public void keyReleased(KeyEvent e) {
+ int key = e.getKeyCode();
+ if (key == KEY_38 || key == KEY_40 || key == KEY_37 || key == KEY_39 || key == KEY_10) //如果是删除符号 ,为了可读性 没有和其他按键的程序相融合
+ {
+ currentPosition = contentTextArea.getCaretPosition();
+ insertPosition = currentPosition;
+ beginPosition = getBeginPosition();
+ } else {
+ if (contentTextArea.getText().trim().length() == 0) {
+ insertPosition = 0;
+ } else {
+ contentTextArea.setForeground(Color.black);
+ currentPosition = contentTextArea.getCaretPosition();
+ beginPosition = getBeginPosition();
+ insertPosition = beginPosition;
+ ifHasBeenWriten = 1;
+ }
+ }
+ }
+
+ private int getBeginPosition() {
+ int i = currentPosition;
+ String textArea = contentTextArea.getText();
+ for (; i > 0; i--) {
+ String tested = textArea.substring(i - 1, i).toUpperCase();
+ char[] testedChar = tested.toCharArray();
+ if (isChar(testedChar[0]) || isNum(testedChar[0])) {
+ continue;
+ } else {
+ break;
+ }
+ }
+ return i;
+ }
+
+ private static boolean isNum(char tested) {
+ return tested >= '0' && tested <= '9';
+ }
+
+ private boolean isChar(char tested) {
+ return tested >= 'A' && tested <= 'Z' || tested >= 'a' && tested < 'z';
+ }
+
+ public class InterfaceAndDescriptionPanel extends JPanel implements AutoCompleteExtraRefreshComponent {
+ @Override
+ public void refresh(String replacementText) {
+ fixInterfaceNameList(replacementText);
+ }
+ }
+
+ private void fixInterfaceNameList(String interfaceName) {
+ DefaultTreeModel defaultTreeModel = (DefaultTreeModel) moduleTree.getModel();
+ TreeNode root = (TreeNode) defaultTreeModel.getRoot();
+ String directCategory = JSAPITreeHelper.getDirectCategory(interfaceName);
+ if (directCategory == null) {
+ return;
+ }
+ setModuleTreeSelection(root, directCategory, defaultTreeModel);
+ interfaceNameModel = (DefaultListModel) interfaceNameList.getModel();
+ interfaceNameModel.clear();
+ List interfaceNames = JSAPITreeHelper.getNames(directCategory);
+ int index = 0;
+ for (int i = 0; i < interfaceNames.size(); i++) {
+ interfaceNameModel.addElement(interfaceNames.get(i));
+ if (StringUtils.equals(interfaceNames.get(i), interfaceName)) {
+ index = i;
+ }
+ }
+ interfaceNameList.setSelectedIndex(index);
+ interfaceNameList.ensureIndexIsVisible(index);
+ }
+
+ private boolean setModuleTreeSelection(TreeNode node, String directCategory, DefaultTreeModel treeModel) {
+
+ DefaultMutableTreeNode defaultMutableTreeNode = (DefaultMutableTreeNode) node;
+ Object userObject = defaultMutableTreeNode.getUserObject();
+ if (userObject instanceof JSAPIUserObject) {
+ String value = ((JSAPIUserObject) userObject).getValue();
+ if (StringUtils.equals(value, directCategory)) {
+ moduleTree.setSelectionPath(new TreePath(treeModel.getPathToRoot(node)));
+ return true;
+ }
+ return false;
+ }
+ for (int i = 0; i < node.getChildCount(); i++) {
+ if (setModuleTreeSelection(node.getChildAt(i), directCategory, treeModel)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private JPanel createInterfaceAndDescriptionPanel() {
+ interfaceAndDescriptionPanel = new InterfaceAndDescriptionPanel();
+ interfaceAndDescriptionPanel.setLayout(new BorderLayout(4, 4));
+ JPanel interfacePanel = new JPanel(new BorderLayout(4, 4));
+ interfaceAndDescriptionPanel.add(interfacePanel, BorderLayout.WEST);
+ JPanel descriptionAndDocumentPanel = new JPanel(new BorderLayout(4, 4));
+ //函数说明和帮助文档框
+ initDescriptionArea(descriptionAndDocumentPanel);
+
+ //模块和接口面板
+ initInterfaceModuleTree(interfacePanel);
+ initInterfaceNameList(interfacePanel);
+
+ initHelpDocumentPane(descriptionAndDocumentPanel);
+
+ interfaceAndDescriptionPanel.add(descriptionAndDocumentPanel, BorderLayout.CENTER);
+ return interfaceAndDescriptionPanel;
+ }
+
+ private void doHelpDocumentSearch() {
+ Object value = interfaceNameList.getSelectedValue();
+ if (value != null) {
+ String url = DOCUMENT_SEARCH_URL + value.toString();
+ try {
+ String result = HttpToolbox.get(url);
+ JSONObject jsonObject = new JSONObject(result);
+ JSONArray jsonArray = jsonObject.optJSONArray("list");
+ if (jsonArray != null) {
+ DefaultListModel helpDOCModel = (DefaultListModel) helpDOCList.getModel();
+ helpDOCModel.clear();
+ for (int i = 0; i < jsonArray.length(); i++) {
+ JSONObject resultJSONObject = jsonArray.optJSONObject(i);
+ String docURL = resultJSONObject.optString("url");
+ String name = resultJSONObject.optString("title").trim();
+ HelpDocument helpDocument = new HelpDocument(docURL, name);
+ helpDOCModel.addElement(helpDocument);
+ }
+ }
+ } catch (JSONException e) {
+ FineLoggerFactory.getLogger().debug(e.getMessage(), e);
+ } catch (Exception e) {
+ FineLoggerFactory.getLogger().error(e.getMessage(), e);
+ }
+ }
+ }
+
+ private void initHelpDocumentPane(JPanel descriptionAndDocumentPanel) {
+ UIScrollPane helpDOCScrollPane;
+ if (isNetworkOk()) {
+ helpDOCList = new JList(new DefaultListModel());
+ initHelpDOCListRender();
+ initHelpDOCListListener();
+ helpDOCScrollPane = new UIScrollPane(helpDOCList);
+ doHelpDocumentSearch();
+ } else {
+ UILabel label1 = new UILabel(Toolkit.i18nText("Fine-Design_Net_Connect_Failed"), 0);
+ label1.setPreferredSize(new Dimension(180, 20));
+ UILabel label2 = new UILabel(Toolkit.i18nText("Fine-Design_Basic_Reload"), 0);
+ label2.setPreferredSize(new Dimension(180, 20));
+ label2.setForeground(Color.blue);
+ JPanel labelPane = FRGUIPaneFactory.createVerticalFlowLayout_Pane(true, 0, 0, 0);
+ labelPane.setBackground(Color.WHITE);
+ labelPane.add(label1);
+ labelPane.add(label2);
+ JPanel containerPanel = FRGUIPaneFactory.createBorderLayout_S_Pane();
+ containerPanel.add(labelPane, BorderLayout.CENTER);
+ helpDOCScrollPane = new UIScrollPane(containerPanel);
+ label2.addMouseListener(new MouseAdapter() {
+ @Override
+ public void mouseClicked(MouseEvent e) {
+ descriptionAndDocumentPanel.removeAll();
+ initHelpDocumentPane(descriptionAndDocumentPanel);
+
+ }
+ });
+ }
+ helpDOCScrollPane.setPreferredSize(new Dimension(200, 200));
+ helpDOCScrollPane.setBorder(null);
+ descriptionAndDocumentPanel.add(this.createNamePane(Toolkit.i18nText("Fine-Design_Relevant_Cases"), helpDOCScrollPane), BorderLayout.EAST);
+
+ }
+
+ private void initHelpDOCListListener() {
+ helpDOCList.addMouseListener(new MouseAdapter() {
+ @Override
+ public void mouseClicked(MouseEvent e) {
+ if (e.getClickCount() == 2) {
+ Object value = helpDOCList.getSelectedValue();
+ if (value instanceof HelpDocument) {
+ String url = ((HelpDocument) value).getDocumentUrl();
+ try {
+ Desktop.getDesktop().browse(new URI(url));
+ } catch (IOException ex) {
+ FineLoggerFactory.getLogger().error(ex.getMessage(), ex);
+ } catch (URISyntaxException ex) {
+ FineLoggerFactory.getLogger().error(ex.getMessage(), ex);
+ }
+ }
+ }
+ }
+ });
+ }
+
+ private void initHelpDOCListRender() {
+ helpDOCList.setCellRenderer(new DefaultListCellRenderer() {
+ @Override
+ public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
+ super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
+ if (value instanceof HelpDocument) {
+ this.setText(((HelpDocument) value).getName());
+ this.setForeground(Color.BLUE);
+ }
+ return this;
+ }
+ });
+ }
+
+ private static boolean isNetworkOk() {
+ try {
+ HttpToolbox.get(URL_FOR_TEST_NETWORK);
+ return true;
+ } catch (Exception ignore) {
+ // 网络异常
+ return false;
+ }
+ }
+
+ private static class HelpDocument {
+ private String documentUrl;
+
+
+ private String name;
+
+ public HelpDocument(String documentUrl, String name) {
+ this.documentUrl = documentUrl;
+ this.name = name;
+ }
+
+ public String getDocumentUrl() {
+ return documentUrl;
+ }
+
+ public String getName() {
+ return name;
+ }
+ }
+
+ private void initDescriptionArea(JPanel descriptionPanel) {
+ descriptionTextArea = new UITextArea();
+ UIScrollPane descriptionScrollPane = new UIScrollPane(descriptionTextArea);
+ descriptionScrollPane.setPreferredSize(new Dimension(300, 200));
+ descriptionPanel.add(this.createNamePane(Toolkit.i18nText("Fine-Design_Interface_Description"), descriptionScrollPane), BorderLayout.CENTER);
+ descriptionTextArea.setBackground(Color.white);
+ descriptionTextArea.setLineWrap(true);
+ descriptionTextArea.setWrapStyleWord(true);
+ descriptionTextArea.setEditable(false);
+ }
+
+ private void installAutoCompletion() {
+ CompletionProvider provider = createCompletionProvider();
+ autoCompletion = new JSImplPaneAutoCompletion(provider);
+ autoCompletion.setListCellRenderer(new CompletionCellRenderer());
+ autoCompletion.install(contentTextArea);
+ autoCompletion.installExtraRefreshComponent(interfaceAndDescriptionPanel);
+ }
+
+ private void uninstallAutoCompletion() {
+ if (autoCompletion != null) {
+ autoCompletion.uninstall();
+ autoCompletion = null;
+ }
+ }
+
+ private CompletionProvider createCompletionProvider() {
+ if (completionProvider == null) {
+ completionProvider = new DefaultCompletionProvider();
+ for (String name : JSAPITreeHelper.getAllNames()) {
+ completionProvider.addCompletion(new BasicCompletion(completionProvider, name));
+ }
+ }
+ return completionProvider;
+ }
+
+ private void initInterfaceModuleTree(JPanel interfacePanel) {
+ moduleTree = new JTree();
+ UIScrollPane moduleTreePane = new UIScrollPane(moduleTree);
+ moduleTreePane.setBorder(new UIRoundedBorder(UIConstants.LINE_COLOR, 1, UIConstants.ARC));
+ interfacePanel.add(this.createNamePane(Toolkit.i18nText("Fine-Design_Module"), moduleTreePane), BorderLayout.WEST);
+ moduleTreePane.setPreferredSize(new Dimension(180, 200));
+
+ moduleTree.setRootVisible(false);
+ moduleTree.setShowsRootHandles(true);
+ moduleTree.setCellRenderer(moduleTreeCellRender);
+ DefaultTreeModel moduleTreeModel = (DefaultTreeModel) moduleTree.getModel();
+ DefaultMutableTreeNode rootNode = (DefaultMutableTreeNode) moduleTreeModel.getRoot();
+ rootNode.removeAllChildren();
+
+ JSAPITreeHelper.createJSAPITree(rootNode);
+ moduleTreeModel.reload();
+
+ initModuleTreeSelectionListener();
+ }
+
+ private void initModuleTreeSelectionListener() {
+ moduleTree.addTreeSelectionListener(new TreeSelectionListener() {
+ @Override
+ public void valueChanged(TreeSelectionEvent e) {
+ DefaultMutableTreeNode selectedTreeNode = (DefaultMutableTreeNode) moduleTree.getLastSelectedPathComponent();
+ Object selectedValue = selectedTreeNode.getUserObject();
+ if (null == selectedValue) {
+ return;
+ }
+ if (selectedValue instanceof JSAPIUserObject) {
+ interfaceNameModel = (DefaultListModel) interfaceNameList.getModel();
+ interfaceNameModel.clear();
+ String text = ((JSAPIUserObject) selectedValue).getValue();
+ List allInterfaceNames = JSAPITreeHelper.getNames(text);
+ for (String interfaceName : allInterfaceNames) {
+ interfaceNameModel.addElement(interfaceName);
+ }
+ if (interfaceNameModel.size() > 0) {
+ interfaceNameList.setSelectedIndex(0);
+ setDescription(interfaceNameList.getSelectedValue().toString());
+ interfaceNameList.ensureIndexIsVisible(0);
+ }
+ }
+ }
+ });
+ }
+
+
+ private DefaultTreeCellRenderer moduleTreeCellRender = new DefaultTreeCellRenderer() {
+ public Component getTreeCellRendererComponent(JTree tree,
+ Object value, boolean selected, boolean expanded,
+ boolean leaf, int row, boolean hasFocus) {
+ super.getTreeCellRendererComponent(tree, value, selected,
+ expanded, leaf, row, hasFocus);
+
+ DefaultMutableTreeNode treeNode = (DefaultMutableTreeNode) value;
+ Object userObj = treeNode.getUserObject();
+ if (userObj instanceof JSAPIUserObject) {
+ this.setText(((JSAPIUserObject) userObj).getDisplayText());
+ this.setIcon(null);
+ }
+ return this;
+ }
+
+ };
+
+
+ private void initInterfaceNameList(JPanel interfacePanel) {
+ interfaceNameList = new JList(new DefaultListModel());
+ UIScrollPane interfaceNamePanelScrollPane = new UIScrollPane(interfaceNameList);
+ interfaceNamePanelScrollPane.setPreferredSize(new Dimension(180, 200));
+ interfacePanel.add(
+ this.createNamePane(Toolkit.i18nText("Fine-Design_Interface") + ":", interfaceNamePanelScrollPane),
+ BorderLayout.CENTER);
+
+ interfaceNamePanelScrollPane.setBorder(new UIRoundedBorder(UIConstants.LINE_COLOR, 1, UIConstants.ARC));
+ initInterfaceNameModule();
+ initInterfaceNameListSelectionListener();
+ initInterfaceNameListMouseListener();
+ }
+
+ private void initInterfaceNameModule() {
+ moduleTree.setSelectionPath(moduleTree.getPathForRow(0));
+ }
+
+ private void setDescription(String interfaceName) {
+ StringBuilder il8Key = new StringBuilder();
+ moduleTree.getSelectionPath().getPath();
+ Object obj = moduleTree.getSelectionPath().getPath()[moduleTree.getSelectionPath().getPath().length - 1];
+ Object userObject = ((DefaultMutableTreeNode) obj).getUserObject();
+ if (userObject instanceof JSAPIUserObject) {
+ il8Key.append(JSAPITreeHelper.getDirectCategory(interfaceName));
+ }
+ interfaceName = interfaceName.toUpperCase();
+ if (!interfaceName.startsWith(SEPARATOR)) {
+ interfaceName = SEPARATOR + interfaceName;
+ }
+ il8Key.append(interfaceName);
+ descriptionTextArea.setText(Toolkit.i18nText(il8Key.toString()));
+ descriptionTextArea.moveCaretPosition(0);
+ }
+
+ private void initInterfaceNameListSelectionListener() {
+ interfaceNameList.addListSelectionListener(new ListSelectionListener() {
+
+ public void valueChanged(ListSelectionEvent evt) {
+ Object selectedValue = interfaceNameList.getSelectedValue();
+ if (selectedValue == null) {
+ return;
+ }
+ String interfaceName = selectedValue.toString();
+ if (!StringUtils.equals(interfaceName, currentValue)) {
+ setDescription(interfaceName);
+ doHelpDocumentSearch();
+ currentValue = interfaceName;
+ }
+
+ }
+ });
+ }
+
+
+ private void initInterfaceNameListMouseListener() {
+ interfaceNameList.addMouseListener(new MouseAdapter() {
+ public void mouseClicked(MouseEvent evt) {
+ if (evt.getClickCount() >= 2) {
+ Object selectedValue = interfaceNameList.getSelectedValue();
+ String interfaceName = selectedValue.toString();
+ applyText(interfaceName);
+ }
+ }
+ });
+ }
+
+ private void applyText(String text) {
+ if (text == null || text.length() <= 0) {
+ return;
+ }
+ if (ifHasBeenWriten == 0) {
+ contentTextArea.setForeground(Color.black);
+ contentTextArea.setText(StringUtils.EMPTY);
+ ifHasBeenWriten = 1;
+ insertPosition = 0;
+ }
+ String textAll = contentTextArea.getText();
+ currentPosition = contentTextArea.getCaretPosition();
+ int insert = 0;
+ int current = 0;
+ if (insertPosition <= currentPosition) {
+ insert = insertPosition;
+ current = currentPosition;
+ } else {
+ insert = currentPosition;
+ current = insertPosition;
+ }
+ String beforeIndexOfInsertString = textAll.substring(0, insert);
+ String afterIndexofInsertString = textAll.substring(current);
+ contentTextArea.setText(beforeIndexOfInsertString + text + afterIndexofInsertString);
+ contentTextArea.getText();
+ contentTextArea.requestFocus();
+ insertPosition = contentTextArea.getCaretPosition();
+ }
+
+ private JPanel createNamePane(String name, JComponent comp) {
+ JPanel namePane = new JPanel(new BorderLayout(4, 4));
+ namePane.add(new UILabel(name), BorderLayout.NORTH);
+ namePane.add(comp, BorderLayout.CENTER);
+ return namePane;
+ }
+
+ private JPanel createTipsPane() {
+ JPanel tipsPane = FRGUIPaneFactory.createBorderLayout_S_Pane();
+ tipsPane.setLayout(new BorderLayout(4, 4));
+ tipsPane.setBorder(BorderFactory.createEmptyBorder(30, 2, 0, 0));
+ JPanel searchPane = FRGUIPaneFactory.createBorderLayout_S_Pane();
+ searchPane.setLayout(new BorderLayout(4, 4));
+ searchPane.add(keyWordTextField, BorderLayout.CENTER);
+
+ //搜索按钮
+ UIButton searchButton = new UIButton(Toolkit.i18nText("Fine-Design_Basic_Search"));
+ searchButton.addActionListener(new ActionListener() {
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ String toFind = keyWordTextField.getText();
+ search(toFind);
+ popTips();
+ tipsList.requestFocusInWindow();
+ }
+ });
+
+ keyWordTextField.addKeyListener(new KeyAdapter() {
+ @Override
+ public void keyPressed(KeyEvent e) {
+ if (e.getKeyChar() == KeyEvent.VK_ENTER) {
+ e.consume();
+ String toFind = keyWordTextField.getText();
+ search(toFind);
+ popTips();
+ tipsList.requestFocusInWindow();
+ }
+ }
+ });
+
+ searchPane.add(searchButton, BorderLayout.EAST);
+ tipsPane.add(searchPane, BorderLayout.NORTH);
+
+ tipsList = new JList(tipsListModel);
+ tipsList.addMouseListener(tipsListMouseListener);
+ tipsList.addListSelectionListener(tipsListSelectionListener);
+ tipsList.addKeyListener(tipListKeyListener);
+
+ return tipsPane;
+ }
+
+ private void search(String key) {
+ tipsListModel.removeAllElements();
+ tipsListModel.clear();
+ key = key.replaceAll(StringUtils.BLANK, StringUtils.EMPTY);
+ ArrayList list = new ArrayList<>();
+ if (!StringUtils.isEmpty(key)) {
+ List allNames = JSAPITreeHelper.getAllNames();
+ for (String name : allNames) {
+ if (searchResult(key, name)) {
+ list.add(name);
+ }
+ }
+ String finalKey = key;
+ Collections.sort(list, new Comparator() {
+ @Override
+ public int compare(String o1, String o2) {
+ int result;
+ boolean o1StartWidth = o1.toLowerCase().startsWith(finalKey.toLowerCase());
+ boolean o2StartWidth = o2.toLowerCase().startsWith(finalKey.toLowerCase());
+ if (o1StartWidth) {
+ result = o2StartWidth ? o1.compareTo(o2) : -1;
+ } else {
+ result = o2StartWidth ? 1 : o1.compareTo(o2);
+ }
+ return result;
+ }
+ });
+ for (String name : list) {
+ tipsListModel.addElement(name);
+ }
+ if (!tipsListModel.isEmpty()) {
+ tipsList.setSelectedIndex(0);
+ }
+ }
+ }
+
+ private boolean searchResult(String key, String interfaceName) {
+ if (StringUtils.isBlank(key) || StringUtils.isBlank(interfaceName)) {
+ return false;
+ }
+ int length = key.length();
+ String temp = interfaceName.toUpperCase();
+ for (int i = 0; i < length; i++) {
+ String check = key.substring(i, i + 1);
+ int index = temp.indexOf(check.toUpperCase());
+ if (index == -1) {
+ return false;
+ } else {
+ temp = temp.substring(index + 1);
+ }
+ }
+ return true;
+ }
+
+ private void initPopTips() {
+ popupMenu = new JPopupMenu();
+ JScrollPane tipsScrollPane = new JScrollPane(tipsList);
+ popupMenu.add(tipsScrollPane);
+ tipsScrollPane.setPreferredSize(new Dimension(220, 146));
+ tipsScrollPane.setBorder(new UIRoundedBorder(UIConstants.LINE_COLOR, 1, UIConstants.ARC));
+ }
+
+ private void popTips() {
+ popupMenu.show(keyWordTextField, 0, 23);
+ }
+
+ private ListSelectionListener tipsListSelectionListener = new ListSelectionListener() {
+ @Override
+ public void valueChanged(ListSelectionEvent e) {
+ Object selectValue = tipsList.getSelectedValue();
+ if (selectValue == null) {
+ return;
+ }
+ String interfaceName = selectValue.toString();
+ fixInterfaceNameList(interfaceName);
+ }
+ };
+
+ private KeyListener tipListKeyListener = new KeyAdapter() {
+ @Override
+ public void keyPressed(KeyEvent e) {
+ if (e.getKeyChar() == KeyEvent.VK_ENTER) {
+ Object selectValue = tipsList.getSelectedValue();
+ if (selectValue == null) {
+ return;
+ }
+ tipListValueSelectAction(selectValue.toString());
+ if (popupMenu != null) {
+ popupMenu.setVisible(false);
+ }
+ contentTextArea.requestFocusInWindow();
+ }
+ }
+ };
+
+ private void tipListValueSelectAction(String value) {
+ if (ifHasBeenWriten == 0) {
+ contentTextArea.setForeground(Color.black);
+ contentTextArea.setText(StringUtils.EMPTY);
+ }
+ contentTextArea.setForeground(Color.black);
+ currentPosition = contentTextArea.getCaretPosition();
+ String output = value;
+ String textAll = contentTextArea.getText();
+ String textReplaced;
+ int position = 0;
+ if (insertPosition <= currentPosition) {
+ textReplaced = textAll.substring(0, insertPosition) + output + textAll.substring(currentPosition);
+ position = insertPosition + output.length();
+ } else {
+ textReplaced = textAll.substring(0, currentPosition) + output + textAll.substring(insertPosition);
+ position = currentPosition + output.length();
+ }
+ contentTextArea.setText(textReplaced);
+ contentTextArea.setCaretPosition(position);
+ insertPosition = position;
+ ifHasBeenWriten = 1;
+ tipsListModel.removeAllElements();
+ }
+
+ private MouseListener tipsListMouseListener = new MouseAdapter() {
+ String singlePressContent;
+
+ String doublePressContent;
+
+ @Override
+ public void mousePressed(MouseEvent e) {
+ int index = tipsList.getSelectedIndex();
+ if (index != -1) {
+ if (e.getClickCount() == 1) {
+ singlePressContent = (String) tipsListModel.getElementAt(index);
+ } else if (e.getClickCount() == 2) {
+ doublePressContent = (String) tipsListModel.getElementAt(index);
+ }
+ }
+ }
+
+ @Override
+ public void mouseReleased(MouseEvent e) {
+ int index = tipsList.getSelectedIndex();
+ if (index != -1) {
+ if (e.getClickCount() == 1) {
+ if (ComparatorUtils.equals((String) tipsListModel.getElementAt(index), singlePressContent)) {
+ singleClickActuator(singlePressContent);
+ }
+ } else if (e.getClickCount() == 2) {
+ if (ComparatorUtils.equals((String) tipsListModel.getElementAt(index), doublePressContent)) {
+ doubleClickActuator(doublePressContent);
+ }
+ if (popupMenu != null) {
+ popupMenu.setVisible(false);
+ }
+ }
+ }
+ }
+
+ private void singleClickActuator(String currentLineContent) {
+ setDescription(currentLineContent);
+ fixInterfaceNameList(currentLineContent);
+ }
+
+ private void doubleClickActuator(String currentLineContent) {
+ tipListValueSelectAction(currentLineContent);
+ }
+ };
+}
diff --git a/designer-base/src/main/java/com/fr/design/javascript/JavaScriptImplPane.java b/designer-base/src/main/java/com/fr/design/javascript/JavaScriptImplPane.java
index 158c05d1e..6a83ad3d2 100644
--- a/designer-base/src/main/java/com/fr/design/javascript/JavaScriptImplPane.java
+++ b/designer-base/src/main/java/com/fr/design/javascript/JavaScriptImplPane.java
@@ -9,6 +9,8 @@ import com.fr.design.gui.itableeditorpane.UITableEditAction;
import com.fr.design.gui.itableeditorpane.UITableEditorPane;
import com.fr.design.gui.itextfield.UITextField;
import com.fr.design.hyperlink.AbstractHyperLinkPane;
+import com.fr.design.javascript.jsapi.JSImplPopulateAction;
+import com.fr.design.javascript.jsapi.JSImplUpdateAction;
import com.fr.design.mainframe.DesignerContext;
import com.fr.design.scrollruler.ModLineBorder;
import com.fr.design.utils.gui.GUICoreUtils;
@@ -17,10 +19,16 @@ import com.fr.js.JavaScriptImpl;
import com.fr.stable.ParameterProvider;
import com.fr.stable.StringUtils;
-import javax.swing.*;
+
+import java.awt.BorderLayout;
+import java.awt.Dimension;
+import java.awt.GridBagConstraints;
+import java.awt.GridLayout;
+import javax.swing.BorderFactory;
+import javax.swing.JPanel;
import javax.swing.event.TableModelEvent;
import javax.swing.event.TableModelListener;
-import java.awt.*;
+
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
@@ -28,11 +36,11 @@ import java.util.List;
public class JavaScriptImplPane extends AbstractHyperLinkPane {
private static final int BOTTOM_BORDER = 12;
private UITextField itemNameTextField;
- private JSContentPane jsPane;
+ protected JSContentPane jsPane;
private UITableEditorPane importedJsPane;
private ReportletParameterViewPane parameterPane;
private String[] defaultArgs;
-
+ private boolean modal;
public JavaScriptImplPane() {
this(new String[0]);
@@ -50,8 +58,57 @@ public class JavaScriptImplPane extends AbstractHyperLinkPane {
initComponents();
}
+ public JavaScriptImplPane(String[] args, boolean modal) {
+ this.modal = modal;
+ this.defaultArgs = args;
+ initComponents();
+ }
+
protected void initComponents() {
- parameterPane = new ReportletParameterViewPane(getChartParaType(), getValueEditorPane(), getValueEditorPane());
+ parameterPane = createParameterViewPane();
+ importedJsPane = createImportedJsPane();
+ importedJsPane.setPreferredSize(new Dimension(265, 150));
+
+ jsPane = createJSContentPane(defaultArgs);
+ jsPane.setBorder(BorderFactory.createTitledBorder(new ModLineBorder(ModLineBorder.TOP), com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Report_JavaScript")));
+
+ parameterPane.setPreferredSize(new Dimension(265, 150));
+ JPanel topPane = new JPanel(new GridLayout(1,2));
+ topPane.add(importedJsPane);
+ topPane.add(parameterPane);
+
+ topPane.setBorder(BorderFactory.createEmptyBorder(0, 0, BOTTOM_BORDER, 0));
+
+ this.setLayout(new BorderLayout());
+ this.add(topPane, BorderLayout.NORTH);
+ this.add(jsPane, BorderLayout.CENTER);
+
+ this.reLayoutForChart();
+ }
+
+ protected JSContentPane createJSContentPane(String[] defaultArgs){
+ JSContentPane jsContentPane= new JSContentPane(defaultArgs,modal);
+ jsContentPane.setJsImplUpdateAction(new JSImplUpdateAction() {
+ @Override
+ public void update(JavaScriptImpl javaScript) {
+ if(javaScript != null){
+ updateBean(javaScript);
+ }
+ }
+ });
+ jsContentPane.setJsImplPopulateAction(new JSImplPopulateAction() {
+ @Override
+ public void populate(JavaScriptImpl javaScript) {
+ if(javaScript != null){
+ populateBean(javaScript);
+ }
+ }
+ });
+ return jsContentPane;
+ }
+
+ protected ReportletParameterViewPane createParameterViewPane(){
+ ReportletParameterViewPane parameterPane = new ReportletParameterViewPane(getChartParaType(), getValueEditorPane(), getValueEditorPane());
parameterPane.setBorder(BorderFactory.createTitledBorder(new ModLineBorder(ModLineBorder.TOP), com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Basic_Parameter")));
parameterPane.addTableEditorListener(new TableModelListener() {
public void tableChanged(TableModelEvent e) {
@@ -72,7 +129,10 @@ public class JavaScriptImplPane extends AbstractHyperLinkPane {
parameterChanger(list);
}
});
+ return parameterPane;
+ }
+ protected UITableEditorPane createImportedJsPane(){
OneListTableModel model = new OneListTableModel(com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Report_ReportServerP_Import_JavaScript"), this) {
public UITableEditAction[] createAction() {
@@ -84,26 +144,12 @@ public class JavaScriptImplPane extends AbstractHyperLinkPane {
return new AddJsAction();
}
};
- importedJsPane = new UITableEditorPane(model);
+ UITableEditorPane importedJsPane = new UITableEditorPane(model);
importedJsPane.setBorder(BorderFactory.createTitledBorder(new ModLineBorder(ModLineBorder.TOP), com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Report_ReportServerP_Import_JavaScript")));
- importedJsPane.setPreferredSize(new Dimension(265, 150));
- jsPane = new JSContentPane(defaultArgs);
- jsPane.setBorder(BorderFactory.createTitledBorder(new ModLineBorder(ModLineBorder.TOP), com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Report_JavaScript")));
+ return importedJsPane;
+ }
- parameterPane.setPreferredSize(new Dimension(265, 150));
- JPanel topPane = GUICoreUtils.createBorderLayoutPane(
- importedJsPane, BorderLayout.CENTER,
- parameterPane, BorderLayout.EAST
- );
- topPane.setPreferredSize(new Dimension(300, 150));
- topPane.setBorder(BorderFactory.createEmptyBorder(0, 0, BOTTOM_BORDER, 0));
- this.setLayout(new BorderLayout());
- this.add(topPane, BorderLayout.NORTH);
- this.add(jsPane, BorderLayout.CENTER);
-
- this.reLayoutForChart();
- }
/**
* 参数改变
@@ -140,10 +186,10 @@ public class JavaScriptImplPane extends AbstractHyperLinkPane {
if (javaScriptImpl == null) {
javaScriptImpl = new JavaScriptImpl();
jsPane.reset();
- }else{
+ } else {
jsPane.populate(javaScriptImpl.getContent());
}
-
+ jsPane.updateJSImpl(javaScriptImpl);
int rowCount = javaScriptImpl.getJSImportSize();
String[] value = new String[rowCount];
for (int i = 0; i < rowCount; i++) {
@@ -160,6 +206,7 @@ public class JavaScriptImplPane extends AbstractHyperLinkPane {
public JavaScriptImpl updateBean() {
JavaScriptImpl javaScript = new JavaScriptImpl();
updateBean(javaScript);
+ jsPane.updateJSImpl(javaScript);
return javaScript;
}
diff --git a/designer-base/src/main/java/com/fr/design/javascript/NewJavaScriptImplPane.java b/designer-base/src/main/java/com/fr/design/javascript/NewJavaScriptImplPane.java
new file mode 100644
index 000000000..23226c6dd
--- /dev/null
+++ b/designer-base/src/main/java/com/fr/design/javascript/NewJavaScriptImplPane.java
@@ -0,0 +1,23 @@
+package com.fr.design.javascript;
+
+
+import com.fr.js.JavaScriptImpl;
+
+
+public class NewJavaScriptImplPane extends JavaScriptImplPane {
+ public NewJavaScriptImplPane(String[] args) {
+ super(args);
+ }
+
+ protected JSContentPane createJSContentPane(String[] defaultArgs){
+ return new JSContentWithDescriptionPane(defaultArgs);
+ }
+
+ public void populate(JavaScriptImpl javaScript) {
+ if (javaScript != null) {
+ populateBean(javaScript);
+ } else {
+ jsPane.reset();
+ }
+ }
+}
diff --git a/designer-base/src/main/java/com/fr/design/javascript/jsapi/CategoryTreeNodesUserObject.java b/designer-base/src/main/java/com/fr/design/javascript/jsapi/CategoryTreeNodesUserObject.java
new file mode 100644
index 000000000..7b568eff7
--- /dev/null
+++ b/designer-base/src/main/java/com/fr/design/javascript/jsapi/CategoryTreeNodesUserObject.java
@@ -0,0 +1,21 @@
+package com.fr.design.javascript.jsapi;
+
+import com.fr.design.i18n.Toolkit;
+
+public class CategoryTreeNodesUserObject implements JSAPIUserObject {
+ private String value;
+
+ public CategoryTreeNodesUserObject(String value) {
+ this.value = value;
+ }
+
+ @Override
+ public String getValue() {
+ return value;
+ }
+
+ @Override
+ public String getDisplayText() {
+ return Toolkit.i18nText(value);
+ }
+}
diff --git a/designer-base/src/main/java/com/fr/design/javascript/jsapi/JSAPITreeHelper.java b/designer-base/src/main/java/com/fr/design/javascript/jsapi/JSAPITreeHelper.java
new file mode 100644
index 000000000..a5def0596
--- /dev/null
+++ b/designer-base/src/main/java/com/fr/design/javascript/jsapi/JSAPITreeHelper.java
@@ -0,0 +1,155 @@
+package com.fr.design.javascript.jsapi;
+
+import com.fr.general.IOUtils;
+import com.fr.json.JSONArray;
+import com.fr.json.JSONObject;
+import com.fr.log.FineLoggerFactory;
+import com.fr.stable.StringUtils;
+import java.io.BufferedReader;
+import java.io.InputStreamReader;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import javax.swing.tree.DefaultMutableTreeNode;
+
+public class JSAPITreeHelper {
+ private static final String JSAPI_PATH = "com/fr/design/javascript/jsapi/jsapi.json";
+ private static final String CATEGORY_PATH = "com/fr/design/javascript/jsapi/category.json";
+ private static JSONObject categoryJSON ;
+ private static JSONObject jsapiJSON ;
+
+ static {
+ jsapiJSON = createJSON(JSAPI_PATH);
+ categoryJSON = createJSON(CATEGORY_PATH);
+ }
+
+ private static JSONObject createJSON(String path) {
+ StringBuilder jsonString = new StringBuilder(StringUtils.EMPTY);
+ try (BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(IOUtils.readResource(path)))) {
+ String s;
+ while ((s = bufferedReader.readLine()) != null) {
+ jsonString.append(s);
+ }
+ } catch (Exception e) {
+ FineLoggerFactory.getLogger().error(e.getMessage(), e);
+ }
+ return new JSONObject(jsonString.toString());
+ }
+
+
+ public static void createJSAPITree(DefaultMutableTreeNode rootNode) {
+ createJSAPITree(categoryJSON, rootNode);
+ }
+
+ public static String getDirectCategory(String name) {
+ if (jsapiJSON != null) {
+ Iterator it = jsapiJSON.keys();
+ while (it.hasNext()) {
+ String key = it.next();
+ JSONArray nameArray = jsapiJSON.optJSONArray(key);
+ for (int i = 0; i < nameArray.length(); i++) {
+ if (StringUtils.equals(nameArray.getString(i), name)) {
+ return key;
+ }
+ }
+ }
+ }
+ return null;
+ }
+
+ private static void createJSAPITree(JSONObject jsonObject, DefaultMutableTreeNode rootNode) {
+ if (jsonObject != null && rootNode != null) {
+ Iterator it = jsonObject.keys();
+ while (it.hasNext()) {
+ String key = it.next();
+ JSONObject subNode = jsonObject.optJSONObject(key);
+ if (subNode.size() == 0) {
+ rootNode.add(new DefaultMutableTreeNode(new CategoryTreeNodesUserObject(key)));
+ } else {
+ DefaultMutableTreeNode treeNode = new DefaultMutableTreeNode(new CategoryTreeNodesUserObject(key));
+ rootNode.add(treeNode);
+ createJSAPITree(subNode, treeNode);
+ }
+ }
+ }
+ }
+
+ private static List getAllSubNodes(String name) {
+ return getAllSubNodes(name, categoryJSON);
+ }
+
+ public static List getAllNames() {
+ ArrayList result = new ArrayList<>();
+ if (jsapiJSON != null) {
+ Iterator it = jsapiJSON.keys();
+ while (it.hasNext()) {
+ String key = it.next();
+ JSONArray nameArray = jsapiJSON.optJSONArray(key);
+ for (int i = 0; i < nameArray.length(); i++) {
+ result.add(nameArray.getString(i));
+ }
+ }
+ }
+ return result;
+ }
+
+ public static List getNames(String category) {
+ ArrayList result = new ArrayList<>();
+ List subCategories = getAllSubNodes(category);
+ if (jsapiJSON != null) {
+ for (String subCategory : subCategories) {
+ if (jsapiJSON.containsKey(subCategory)) {
+ JSONArray nameArray = jsapiJSON.optJSONArray(subCategory);
+ for (int i = 0; i < nameArray.length(); i++) {
+ result.add(nameArray.getString(i));
+ }
+ }
+ }
+ }
+ return result;
+ }
+
+ private static List getAllSubNodes(String name, JSONObject jsonObject) {
+ ArrayList result = new ArrayList<>();
+ if (jsonObject != null) {
+ Iterator it = jsonObject.keys();
+ while (it.hasNext()) {
+ String key = it.next();
+ JSONObject subNode = jsonObject.optJSONObject(key);
+ if (subNode.size() == 0) {
+ if (StringUtils.equals(key, name)) {
+ result.add(key);
+ return result;
+ }
+ } else {
+ if (StringUtils.equals(key, name)) {
+ result.add(key);
+ result.addAll(getAllSubNodes(subNode));
+ return result;
+ } else {
+ result.addAll(getAllSubNodes(name, subNode));
+ }
+ }
+ }
+ }
+ return result;
+ }
+
+ private static List getAllSubNodes(JSONObject jsonObject) {
+ ArrayList result = new ArrayList<>();
+ if (jsonObject != null) {
+ Iterator it = jsonObject.keys();
+ while (it.hasNext()) {
+ String key = it.next();
+ JSONObject subNode = jsonObject.optJSONObject(key);
+ if (subNode.size() == 0) {
+ result.add(key);
+ } else {
+ result.add(key);
+ result.addAll(getAllSubNodes(subNode));
+ }
+ }
+ }
+ return result;
+ }
+}
diff --git a/designer-base/src/main/java/com/fr/design/javascript/jsapi/JSAPIUserObject.java b/designer-base/src/main/java/com/fr/design/javascript/jsapi/JSAPIUserObject.java
new file mode 100644
index 000000000..6790ec71e
--- /dev/null
+++ b/designer-base/src/main/java/com/fr/design/javascript/jsapi/JSAPIUserObject.java
@@ -0,0 +1,9 @@
+package com.fr.design.javascript.jsapi;
+
+
+public interface JSAPIUserObject {
+
+ String getValue();
+
+ String getDisplayText();
+}
diff --git a/designer-base/src/main/java/com/fr/design/javascript/jsapi/JSImplPopulateAction.java b/designer-base/src/main/java/com/fr/design/javascript/jsapi/JSImplPopulateAction.java
new file mode 100644
index 000000000..c33d7fe0e
--- /dev/null
+++ b/designer-base/src/main/java/com/fr/design/javascript/jsapi/JSImplPopulateAction.java
@@ -0,0 +1,7 @@
+package com.fr.design.javascript.jsapi;
+
+import com.fr.js.JavaScriptImpl;
+
+public interface JSImplPopulateAction {
+ void populate(JavaScriptImpl javaScript);
+}
diff --git a/designer-base/src/main/java/com/fr/design/javascript/jsapi/JSImplUpdateAction.java b/designer-base/src/main/java/com/fr/design/javascript/jsapi/JSImplUpdateAction.java
new file mode 100644
index 000000000..b812d8b87
--- /dev/null
+++ b/designer-base/src/main/java/com/fr/design/javascript/jsapi/JSImplUpdateAction.java
@@ -0,0 +1,7 @@
+package com.fr.design.javascript.jsapi;
+
+import com.fr.js.JavaScriptImpl;
+
+public interface JSImplUpdateAction {
+ void update(JavaScriptImpl javaScript);
+}
diff --git a/designer-base/src/main/resources/com/fr/design/javascript/jsapi/category.json b/designer-base/src/main/resources/com/fr/design/javascript/jsapi/category.json
new file mode 100644
index 000000000..ad1792976
--- /dev/null
+++ b/designer-base/src/main/resources/com/fr/design/javascript/jsapi/category.json
@@ -0,0 +1,47 @@
+{
+ "Fine-Design_JSAPI_Public_Module": {
+ "Fine-Design_JSAPI_Public_Module_Global": {
+ "Fine-Design_JSAPI_Public_Module_Global_Universal": {},
+ "Fine-Design_JSAPI_Public_Module_Global_FR": {},
+ "Fine-Design_JSAPI_Public_Module_Global_FS": {},
+ "Fine-Design_JSAPI_Public_Module_Global_Mobile": {}
+ },
+ "Fine-Design_JSAPI_Public_Module_Widget": {
+ "Fine-Design_JSAPI_Public_Module_Widget_Get": {},
+ "Fine-Design_JSAPI_Public_Module_Widget_Universal": {},
+ "Fine-Design_JSAPI_Public_Module_Date_Widget_Peculiar": {},
+ "Fine-Design_JSAPI_Public_Module_Button_Widget_Peculiar": {},
+ "Fine-Design_JSAPI_Public_Module_Combobox_Widget_Peculiar": {}
+ },
+ "Fine-Design_JSAPI_Public_Module_Table": {
+ "Fine-Design_JSAPI_Public_Module_Table_Marquee": {},
+ "Fine-Design_JSAPI_Public_Module_Table_Scrollbar": {},
+ "Fine-Design_JSAPI_Public_Module_Table_Cell_Style": {},
+ "Fine-Design_JSAPI_Public_Module_Table_Row_Height_Col_Width": {},
+ "Fine-Design_JSAPI_Public_Module_Table_Cell_Value": {},
+ "Fine-Design_JSAPI_Public_Module_Table_Cell_Radius": {}
+ },
+ "Fine-Design_JSAPI_Public_Module_Toolbar": {
+ "Fine-Design_JSAPI_Public_Module_Toolbar_Email_Button": {}
+ },
+ "Fine-Design_JSAPI_Public_Module_Report_Page": {
+ "Fine-Design_JSAPI_Public_Module_Report_Page_Jump": {},
+ "Fine-Design_JSAPI_Public_Module_Report_Page_Number_Get": {}
+ },
+ "Fine-Design_JSAPI_Public_Module_Report_Export": {}
+ },
+ "Fine-Design_JSAPI_Cpt": {
+ "Fine-Design_JSAPI_Cpt_Page_Preview": {
+ "Fine-Design_JSAPI_Cpt_Page_Preview_Folding_Tree": {}
+ },
+ "Fine-Design_JSAPI_Cpt_Write_Preview": {},
+ "Fine-Design_JSAPI_Cpt_View_Preview": {
+ "Fine-Design_JSAPI_Cpt_View_Preview_Report_Location": {}
+ }
+ },
+ "Fine-Design_JSAPI_Form": {
+ "Fine-Design_JSAPI_Form_Component_Get": {},
+ "Fine-Design_JSAPI_Form_Component_Universal": {},
+ "Fine-Design_JSAPI_Form_Component_Tab": {}
+ }
+}
\ No newline at end of file
diff --git a/designer-base/src/main/resources/com/fr/design/javascript/jsapi/jsapi.json b/designer-base/src/main/resources/com/fr/design/javascript/jsapi/jsapi.json
new file mode 100644
index 000000000..e268f70e1
--- /dev/null
+++ b/designer-base/src/main/resources/com/fr/design/javascript/jsapi/jsapi.json
@@ -0,0 +1,33 @@
+{
+ "Fine-Design_JSAPI_Public_Module_Global_Universal": ["_g()", "getParameterContainer", "parameterCommit", "loadContentPane", "getPreviewType"],
+ "Fine-Design_JSAPI_Public_Module_Global_FR": [ "servletURL", "serverURL", "server", "fineServletURL", "SessionMgr.getSessionID", "showDialog", "closeDialog",
+ "doHyperlinkByGet", "doHyperlinkByPost", "doURLPrint", "Msg", "remoteEvaluate", "jsonEncode", "jsonDecode",
+ "ajax", "isEmpty", "isArray", "cellStr2ColumnRow", "columnRow2CellStr"],
+ "Fine-Design_JSAPI_Public_Module_Global_FS": ["signOut", "tabPane.closeActiveTab", "tabPane.addItem"],
+ "Fine-Design_JSAPI_Public_Module_Global_Mobile": ["location", "Mobile.getDeviceInfo"],
+ "Fine-Design_JSAPI_Public_Module_Widget_Get": ["this", "this.options.form", "getWidgetByName"],
+ "Fine-Design_JSAPI_Public_Module_Widget_Universal": ["getValue", "getText", "setValue", "visible", "invisible", "setVisible", "isVisible", "setEnable", "isEnabled",
+ "reset", "getType", "setWaterMark", "fireEvent", "setPopupStyle"],
+ "Fine-Design_JSAPI_Public_Module_Date_Widget_Peculiar":["setMaxAndMinDate"],
+ "Fine-Design_JSAPI_Public_Module_Button_Widget_Peculiar":["doClick"],
+ "Fine-Design_JSAPI_Public_Module_Combobox_Widget_Peculiar":["setName4Empty"],
+ "Fine-Design_JSAPI_Public_Module_Table_Marquee":["startMarquee", "stopMarquee"],
+ "Fine-Design_JSAPI_Public_Module_Table_Scrollbar":["setHScrollBarVisible", "setVScrollBarVisible"],
+ "Fine-Design_JSAPI_Public_Module_Table_Cell_Style":["addEffect"],
+ "Fine-Design_JSAPI_Public_Module_Table_Row_Height_Col_Width":["setRowHeight", "setColWidth"],
+ "Fine-Design_JSAPI_Public_Module_Table_Cell_Value":["getCellValue", "setCellValue"],
+ "Fine-Design_JSAPI_Public_Module_Table_Cell_Radius":["setCellRadius"],
+ "Fine-Design_JSAPI_Public_Module_Toolbar":["toolBarFloat", "setStyle","getToolbar"],
+ "Fine-Design_JSAPI_Public_Module_Toolbar_Email_Button":["changeFormat"],
+ "Fine-Design_JSAPI_Public_Module_Report_Page_Jump":["gotoPreviousPage", "gotoNextPage", "gotoLastPage", "gotoFirstPage", "gotoPage"],
+ "Fine-Design_JSAPI_Public_Module_Report_Page_Number_Get":["getCurrentPageIndex", "getReportTotalPage", "currentPageIndex", "reportTotalPage"],
+ "Fine-Design_JSAPI_Public_Module_Report_Export":["exportReportToExcel", "exportReportToImage", "exportReportToPDF", "exportReportToWord"],
+ "Fine-Design_JSAPI_Cpt_Page_Preview_Folding_Tree":["expandNodeLayer", "collapseNodeLayer", "expandAllNodeLayer", "collapseAllNodeLayer"],
+ "Fine-Design_JSAPI_Cpt_Write_Preview":["getWidgetByCell", "appendReportRC", "appendReportRow",
+ "deleteReportRC", "deleteRows", "refreshAllSheets", "loadSheetByIndex", "loadSheetByName", "isDirtyPage",
+ "isAutoStash", "writeReport", "verifyAndWriteReport", "verifyReport", "importExcel", "importExcel_Clean",
+ "importExcel_Append", "importExcel_Cover", "stash", "clear"],
+ "Fine-Design_JSAPI_Cpt_View_Preview_Report_Location":["centerReport"],
+ "Fine-Design_JSAPI_Form_Component_Get":["getAllWidgets"],
+ "Fine-Design_JSAPI_Form_Component_Tab":["showCardByIndex", "showCardByIndex", "getShowIndex", "setTitleVisible"]
+}
\ No newline at end of file
diff --git a/designer-base/src/test/java/com/fr/design/javascript/jsapi/JSAPITreeHelperTest.java b/designer-base/src/test/java/com/fr/design/javascript/jsapi/JSAPITreeHelperTest.java
new file mode 100644
index 000000000..e0d5ae7c9
--- /dev/null
+++ b/designer-base/src/test/java/com/fr/design/javascript/jsapi/JSAPITreeHelperTest.java
@@ -0,0 +1,31 @@
+package com.fr.design.javascript.jsapi;
+
+import java.util.List;
+import javax.swing.tree.DefaultMutableTreeNode;
+import junit.framework.TestCase;
+
+public class JSAPITreeHelperTest extends TestCase {
+ public void testGetName(){
+ List names = JSAPITreeHelper.getNames("Fine-Design_JSAPI_Public_Module_Toolbar");
+ assertEquals(names.size(),4);
+ assertTrue(names.contains( "toolBarFloat"));
+ assertTrue(names.contains( "setStyle"));
+ assertTrue(names.contains( "getToolbar"));
+ assertTrue(names.contains( "changeFormat"));
+ List allNames = JSAPITreeHelper.getAllNames();
+ assertEquals(allNames.size(),16);
+ }
+
+ public void testGetDirectCategory(){
+ String directCategory = JSAPITreeHelper.getDirectCategory("_g()");
+ assertEquals(directCategory,"Fine-Design_JSAPI_Public_Module_Global_Universal");
+ directCategory = JSAPITreeHelper.getDirectCategory("showCardByIndex");
+ assertEquals(directCategory,"Fine-Design_JSAPI_Form_Component_Tab");
+ }
+
+ public void testCreateJSAPITree(){
+ DefaultMutableTreeNode rootNode = new DefaultMutableTreeNode();
+ JSAPITreeHelper.createJSAPITree(rootNode);
+ assertEquals(2,rootNode.getChildCount());
+ }
+}
diff --git a/designer-base/src/test/resources/com/fr/design/javascript/jsapi/category.json b/designer-base/src/test/resources/com/fr/design/javascript/jsapi/category.json
new file mode 100644
index 000000000..2d0e50ba0
--- /dev/null
+++ b/designer-base/src/test/resources/com/fr/design/javascript/jsapi/category.json
@@ -0,0 +1,17 @@
+{
+ "Fine-Design_JSAPI_Public_Module": {
+ "Fine-Design_JSAPI_Public_Module_Global": {
+ "Fine-Design_JSAPI_Public_Module_Global_Universal": {},
+ "Fine-Design_JSAPI_Public_Module_Global_Mobile": {}
+ },
+ "Fine-Design_JSAPI_Public_Module_Widget": {
+ "Fine-Design_JSAPI_Public_Module_Date_Widget_Peculiar": {}
+ },
+ "Fine-Design_JSAPI_Public_Module_Toolbar": {
+ "Fine-Design_JSAPI_Public_Module_Toolbar_Email_Button": {}
+ }
+ },
+ "Fine-Design_JSAPI_Form": {
+ "Fine-Design_JSAPI_Form_Component_Tab": {}
+ }
+}
\ No newline at end of file
diff --git a/designer-base/src/test/resources/com/fr/design/javascript/jsapi/jsapi.json b/designer-base/src/test/resources/com/fr/design/javascript/jsapi/jsapi.json
new file mode 100644
index 000000000..4ff0a321e
--- /dev/null
+++ b/designer-base/src/test/resources/com/fr/design/javascript/jsapi/jsapi.json
@@ -0,0 +1,8 @@
+{
+ "Fine-Design_JSAPI_Public_Module_Global_Universal": ["_g()", "getParameterContainer", "parameterCommit", "loadContentPane", "getPreviewType"],
+ "Fine-Design_JSAPI_Public_Module_Global_Mobile": ["location", "Mobile.getDeviceInfo"],
+ "Fine-Design_JSAPI_Public_Module_Date_Widget_Peculiar":["setMaxAndMinDate"],
+ "Fine-Design_JSAPI_Public_Module_Toolbar":["toolBarFloat", "setStyle","getToolbar"],
+ "Fine-Design_JSAPI_Public_Module_Toolbar_Email_Button":["changeFormat"],
+ "Fine-Design_JSAPI_Form_Component_Tab":["showCardByIndex", "showCardByIndex", "getShowIndex", "setTitleVisible"]
+}
\ No newline at end of file
diff --git a/designer-realize/src/main/java/com/fr/design/javascript/ListenerEditPane.java b/designer-realize/src/main/java/com/fr/design/javascript/ListenerEditPane.java
index 095d011f7..056707896 100644
--- a/designer-realize/src/main/java/com/fr/design/javascript/ListenerEditPane.java
+++ b/designer-realize/src/main/java/com/fr/design/javascript/ListenerEditPane.java
@@ -89,7 +89,7 @@ public class ListenerEditPane extends BasicBeanPane {
card = new CardLayout();
hyperlinkPane = FRGUIPaneFactory.createCardLayout_S_Pane();
hyperlinkPane.setLayout(card);
- JavaScriptImplPane javaScriptPane = new JavaScriptImplPane(defaultArgs);
+ JavaScriptImplPane javaScriptPane = new JavaScriptImplPane(defaultArgs,true);
hyperlinkPane.add(JS, javaScriptPane);
// 提交入库
List dbManiList = new ArrayList();