Browse Source

Pull request #7452: REPORT-58833 js编辑器

Merge in DESIGN/design from ~PENGDA/design:feature/x to feature/x

* commit 'a6c350c17c78a831886f69fe450b3bf8acca1624':
  REPORT-58833 js编辑器
  REPORT-58833 js编辑器
feature/x
pengda 3 years ago
parent
commit
b6d0b7715c
  1. 10
      designer-base/src/main/java/com/fr/design/formula/FormulaPane.java
  2. 5
      designer-base/src/main/java/com/fr/design/gui/autocomplete/AutoCompleteExtraRefreshComponent.java
  3. 982
      designer-base/src/main/java/com/fr/design/gui/autocomplete/AutoCompleteWithExtraRefreshPopupWindow.java
  4. 1260
      designer-base/src/main/java/com/fr/design/gui/autocomplete/AutoCompletionWithExtraRefresh.java
  5. 976
      designer-base/src/main/java/com/fr/design/gui/autocomplete/FormulaAutoCompletePopupWindow.java
  6. 1241
      designer-base/src/main/java/com/fr/design/gui/autocomplete/FormulaPaneAutoCompletion.java
  7. 16
      designer-base/src/main/java/com/fr/design/gui/autocomplete/JSAutoCompletePopupWindow.java
  8. 31
      designer-base/src/main/java/com/fr/design/gui/autocomplete/JSImplPaneAutoCompletion.java
  9. 167
      designer-base/src/main/java/com/fr/design/javascript/JSContentPane.java
  10. 858
      designer-base/src/main/java/com/fr/design/javascript/JSContentWithDescriptionPane.java
  11. 95
      designer-base/src/main/java/com/fr/design/javascript/JavaScriptImplPane.java
  12. 23
      designer-base/src/main/java/com/fr/design/javascript/NewJavaScriptImplPane.java
  13. 21
      designer-base/src/main/java/com/fr/design/javascript/jsapi/CategoryTreeNodesUserObject.java
  14. 155
      designer-base/src/main/java/com/fr/design/javascript/jsapi/JSAPITreeHelper.java
  15. 9
      designer-base/src/main/java/com/fr/design/javascript/jsapi/JSAPIUserObject.java
  16. 7
      designer-base/src/main/java/com/fr/design/javascript/jsapi/JSImplPopulateAction.java
  17. 7
      designer-base/src/main/java/com/fr/design/javascript/jsapi/JSImplUpdateAction.java
  18. 47
      designer-base/src/main/resources/com/fr/design/javascript/jsapi/category.json
  19. 33
      designer-base/src/main/resources/com/fr/design/javascript/jsapi/jsapi.json
  20. 31
      designer-base/src/test/java/com/fr/design/javascript/jsapi/JSAPITreeHelperTest.java
  21. 17
      designer-base/src/test/resources/com/fr/design/javascript/jsapi/category.json
  22. 8
      designer-base/src/test/resources/com/fr/design/javascript/jsapi/jsapi.json
  23. 2
      designer-realize/src/main/java/com/fr/design/javascript/ListenerEditPane.java

10
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);
}
/*
* 查看函数的详细信息
*/

5
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);
}

982
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 <code>null</code> 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 <code>key</code>.
* @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<Completion> completions) {
model.setContents(completions);
selectFirstItem();
}
/**
* Sets the size of the description window.
*
* @param size The new size. This cannot be <code>null</code>.
*/
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 <code>null</code>,
* 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 <tt>LookAndFeel</tt> 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();
}
}
}
}

1260
designer-base/src/main/java/com/fr/design/gui/autocomplete/AutoCompletionWithExtraRefresh.java

File diff suppressed because it is too large Load Diff

976
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 <code>null</code> 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 <code>key</code>.
* @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<Completion> completions) {
model.setContents(completions);
selectFirstItem();
}
/**
* Sets the size of the description window.
*
* @param size The new size. This cannot be <code>null</code>.
*/
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 <code>null</code>,
* 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 <tt>LookAndFeel</tt> 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();
}
}
}
}

1241
designer-base/src/main/java/com/fr/design/gui/autocomplete/FormulaPaneAutoCompletion.java

File diff suppressed because it is too large Load Diff

16
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);
}
}

31
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
* <code>null</code>.
*/
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;
}
}

167
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) {

858
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<String> 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<String> 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<String> list = new ArrayList<>();
if (!StringUtils.isEmpty(key)) {
List<String> allNames = JSAPITreeHelper.getAllNames();
for (String name : allNames) {
if (searchResult(key, name)) {
list.add(name);
}
}
String finalKey = key;
Collections.sort(list, new Comparator<String>() {
@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);
}
};
}

95
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<JavaScriptImpl> {
private static final int BOTTOM_BORDER = 12;
private UITextField itemNameTextField;
private JSContentPane jsPane;
protected JSContentPane jsPane;
private UITableEditorPane<String> 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<JavaScriptImpl> {
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<JavaScriptImpl> {
parameterChanger(list);
}
});
return parameterPane;
}
protected UITableEditorPane<String> createImportedJsPane(){
OneListTableModel<String> model = new OneListTableModel<String>(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<JavaScriptImpl> {
return new AddJsAction();
}
};
importedJsPane = new UITableEditorPane<String>(model);
UITableEditorPane<String> importedJsPane = new UITableEditorPane<String>(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<JavaScriptImpl> {
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<JavaScriptImpl> {
public JavaScriptImpl updateBean() {
JavaScriptImpl javaScript = new JavaScriptImpl();
updateBean(javaScript);
jsPane.updateJSImpl(javaScript);
return javaScript;
}

23
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();
}
}
}

21
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);
}
}

155
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<String> 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<String> 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<String> getAllSubNodes(String name) {
return getAllSubNodes(name, categoryJSON);
}
public static List<String> getAllNames() {
ArrayList<String> result = new ArrayList<>();
if (jsapiJSON != null) {
Iterator<String> 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<String> getNames(String category) {
ArrayList<String> result = new ArrayList<>();
List<String> 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<String> getAllSubNodes(String name, JSONObject jsonObject) {
ArrayList<String> result = new ArrayList<>();
if (jsonObject != null) {
Iterator<String> 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<String> getAllSubNodes(JSONObject jsonObject) {
ArrayList<String> result = new ArrayList<>();
if (jsonObject != null) {
Iterator<String> 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;
}
}

9
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();
}

7
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);
}

7
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);
}

47
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": {}
}
}

33
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"]
}

31
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<String> 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<String> 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());
}
}

17
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": {}
}
}

8
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"]
}

2
designer-realize/src/main/java/com/fr/design/javascript/ListenerEditPane.java

@ -89,7 +89,7 @@ public class ListenerEditPane extends BasicBeanPane<Listener> {
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();

Loading…
Cancel
Save