diff --git a/designer-base/src/main/java/com/fr/design/actions/file/PreferencePane.java b/designer-base/src/main/java/com/fr/design/actions/file/PreferencePane.java
index 80f217a31a..5a777b895d 100644
--- a/designer-base/src/main/java/com/fr/design/actions/file/PreferencePane.java
+++ b/designer-base/src/main/java/com/fr/design/actions/file/PreferencePane.java
@@ -809,7 +809,7 @@ public class PreferencePane extends BasicPane {
designerEnvManager.setJoinProductImprove(this.joinProductImproveCheckBox.isSelected());
designerEnvManager.setEmbedServerLazyStartup(this.embedServerLazyStartupCheckBox.isSelected());
designerEnvManager.setImageCompress(this.imageCompressPanelCheckBox.isSelected());
- designerEnvManager.setUseOptimizedUPM4Adapter(this.useOptimizedUPMCheckbox.isSelected());
+ designerEnvManager.setUseOptimizedUPM4Adapter(this.useOptimizedUPMCheckbox != null && this.useOptimizedUPMCheckbox.isSelected());
VcsConfigManager vcsConfigManager = designerEnvManager.getVcsConfigManager();
vcsConfigManager.setSaveInterval(this.saveIntervalEditor.getValue());
vcsConfigManager.setVcsEnable(this.vcsEnableCheckBox.isSelected());
diff --git a/designer-base/src/main/java/com/fr/design/cell/CellStylePreviewPane.java b/designer-base/src/main/java/com/fr/design/cell/CellStylePreviewPane.java
index b4b382af5b..eb3c2cd7b5 100644
--- a/designer-base/src/main/java/com/fr/design/cell/CellStylePreviewPane.java
+++ b/designer-base/src/main/java/com/fr/design/cell/CellStylePreviewPane.java
@@ -19,9 +19,7 @@ import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
-import java.awt.Rectangle;
import java.awt.RenderingHints;
-import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;
import java.util.List;
@@ -154,16 +152,16 @@ public class CellStylePreviewPane extends JPanel {
float adjustRight = 0;
float adjustBottom = 0;
if (column == 0) {
- adjustLeft = GraphHelper.getLineStyleSize(style.getBorderLeft()) / 2.0F + computeOffset4DoubleStyleBorder(style);
+ adjustLeft = computeHalfSize4StyledBorder(style.getBorderLeft());
}
if (row == 0) {
- adjustTop = GraphHelper.getLineStyleSize(style.getBorderTop()) / 2.0F + computeOffset4DoubleStyleBorder(style);
+ adjustTop = computeHalfSize4StyledBorder(style.getBorderTop());
}
if (column == columnSpan - 1) {
- adjustRight = -GraphHelper.getLineStyleSize(style.getBorderRight()) / 2.0F - computeOffset4DoubleStyleBorder(style);
+ adjustRight = -computeHalfSize4StyledBorder(style.getBorderRight());
}
if (row == rowSpan - 1) {
- adjustBottom = -GraphHelper.getLineStyleSize(style.getBorderBottom()) / 2.0F - computeOffset4DoubleStyleBorder(style);
+ adjustBottom = -computeHalfSize4StyledBorder(style.getBorderBottom());
}
g2d.translate(adjustLeft, adjustTop);
@@ -171,15 +169,16 @@ public class CellStylePreviewPane extends JPanel {
g2d.translate(-adjustLeft, -adjustTop);
}
- private float computeOffset4DoubleStyleBorder(Style style) {
- float offset = 0F;
- if (style.getBorderLeft() == Constants.LINE_DOUBLE) {
- offset += GraphHelper.getLineStyleSize(Constants.LINE_THIN) / 2.0F;
- } else if (style.getBorderLeft() == Constants.LINE_DOUBLE_DOT) {
- offset += GraphHelper.getLineStyleSize(Constants.LINE_DOT) / 2.0F;
+ private float computeHalfSize4StyledBorder(int border) {
+ float size = GraphHelper.getLineStyleSize(border) / 2.0F;
+
+ if (border == Constants.LINE_DOUBLE) {
+ size += GraphHelper.getLineStyleSize(Constants.LINE_THIN) / 2.0F;
+ } else if (border == Constants.LINE_DOUBLE_DOT) {
+ size += GraphHelper.getLineStyleSize(Constants.LINE_DOT) / 2.0F;
}
- return offset;
+ return size;
}
@Override
diff --git a/designer-base/src/main/java/com/fr/design/formula/FormulaPane.java b/designer-base/src/main/java/com/fr/design/formula/FormulaPane.java
index 904cdf00b0..da61fab157 100644
--- a/designer-base/src/main/java/com/fr/design/formula/FormulaPane.java
+++ b/designer-base/src/main/java/com/fr/design/formula/FormulaPane.java
@@ -16,6 +16,7 @@ import com.fr.design.dialog.BasicPane;
import com.fr.design.dialog.DialogActionAdapter;
import com.fr.design.dialog.FineJOptionPane;
import com.fr.design.file.HistoryTemplateListCache;
+import com.fr.design.gui.autocomplete.AutoCompleteExtraRefreshComponent;
import com.fr.design.gui.autocomplete.CompletionCellRenderer;
import com.fr.design.gui.autocomplete.CompletionProvider;
import com.fr.design.gui.autocomplete.DefaultCompletionProvider;
@@ -416,7 +417,7 @@ public class FormulaPane extends BasicPane implements KeyListener, UIFormula {
autoCompletion = new FormulaPaneAutoCompletion(provider);
autoCompletion.setListCellRenderer(new CompletionCellRenderer());
autoCompletion.install(formulaTextArea);
- autoCompletion.installVariableTree(variableTreeAndDescriptionArea);
+ autoCompletion.installExtraRefreshComponent(variableTreeAndDescriptionArea);
}
@@ -1036,7 +1037,7 @@ public class FormulaPane extends BasicPane implements KeyListener, UIFormula {
}
}
- public class VariableTreeAndDescriptionArea extends JPanel {
+ public class VariableTreeAndDescriptionArea extends JPanel implements AutoCompleteExtraRefreshComponent {
private JTree variablesTree;
private UITextArea descriptionTextArea;
@@ -1277,6 +1278,11 @@ public class FormulaPane extends BasicPane implements KeyListener, UIFormula {
functionTypeList.setSelectedIndex(0);
}
+ @Override
+ public void refresh(String replacementText) {
+ refreshText(replacementText);
+ }
+
/*
* 查看函数的详细信息
*/
diff --git a/designer-base/src/main/java/com/fr/design/gui/autocomplete/AutoCompleteExtraRefreshComponent.java b/designer-base/src/main/java/com/fr/design/gui/autocomplete/AutoCompleteExtraRefreshComponent.java
new file mode 100644
index 0000000000..ff6b7faef3
--- /dev/null
+++ b/designer-base/src/main/java/com/fr/design/gui/autocomplete/AutoCompleteExtraRefreshComponent.java
@@ -0,0 +1,5 @@
+package com.fr.design.gui.autocomplete;
+
+public interface AutoCompleteExtraRefreshComponent {
+ void refresh(String replacementText);
+}
diff --git a/designer-base/src/main/java/com/fr/design/gui/autocomplete/AutoCompleteWithExtraRefreshPopupWindow.java b/designer-base/src/main/java/com/fr/design/gui/autocomplete/AutoCompleteWithExtraRefreshPopupWindow.java
new file mode 100644
index 0000000000..3f4f191777
--- /dev/null
+++ b/designer-base/src/main/java/com/fr/design/gui/autocomplete/AutoCompleteWithExtraRefreshPopupWindow.java
@@ -0,0 +1,982 @@
+package com.fr.design.gui.autocomplete;
+
+import com.fr.design.gui.syntax.ui.rsyntaxtextarea.PopupWindowDecorator;
+import com.fr.log.FineLoggerFactory;
+import java.awt.BorderLayout;
+import java.awt.ComponentOrientation;
+import java.awt.Dimension;
+import java.awt.Point;
+import java.awt.Rectangle;
+import java.awt.Toolkit;
+import java.awt.Window;
+import java.awt.event.ActionEvent;
+import java.awt.event.KeyEvent;
+import java.awt.event.MouseEvent;
+import java.awt.event.MouseListener;
+import java.util.List;
+import javax.swing.AbstractAction;
+import javax.swing.Action;
+import javax.swing.ActionMap;
+import javax.swing.InputMap;
+import javax.swing.JComponent;
+import javax.swing.JList;
+import javax.swing.JPanel;
+import javax.swing.JScrollPane;
+import javax.swing.JWindow;
+import javax.swing.KeyStroke;
+import javax.swing.ListCellRenderer;
+import javax.swing.SwingUtilities;
+import javax.swing.event.CaretEvent;
+import javax.swing.event.CaretListener;
+import javax.swing.event.ListSelectionEvent;
+import javax.swing.event.ListSelectionListener;
+import javax.swing.plaf.ListUI;
+import javax.swing.text.Caret;
+import javax.swing.text.JTextComponent;
+
+public abstract class AutoCompleteWithExtraRefreshPopupWindow extends JWindow implements CaretListener,
+ ListSelectionListener, MouseListener {
+ private final static int DIS = 5;
+ /**
+ * The parent AutoCompletion instance.
+ */
+ private AutoCompletion ac;
+
+ /**
+ * The list of completion choices.
+ */
+ private JList list;
+
+ /**
+ * The contents of {@link #list()}.
+ */
+ private CompletionListModel model;
+
+ /**
+ * A hack to work around the fact that we clear our completion model (and
+ * our selection) when hiding the completion window. This allows us to
+ * still know what the user selected after the popup is hidden.
+ */
+ private Completion lastSelection;
+
+ /**
+ * Optional popup window containing a description of the currently
+ * selected completion.
+ */
+ private AutoCompleteDescWindow descWindow;
+
+ /**
+ * The preferred size of the optional description window. This field
+ * only exists because the user may (and usually will) set the size of
+ * the description window before it exists (it must be parented to a
+ * Window).
+ */
+ private Dimension preferredDescWindowSize;
+
+
+
+ /**
+ * Whether the completion window and the optional description window
+ * should be displayed above the current caret position (as opposed to
+ * underneath it, which is preferred unless there is not enough space).
+ */
+ private boolean aboveCaret;
+
+ private int lastLine;
+
+ private KeyActionPair escapeKap;
+ private KeyActionPair upKap;
+ private KeyActionPair downKap;
+ private KeyActionPair leftKap;
+ private KeyActionPair rightKap;
+ private KeyActionPair enterKap;
+ private KeyActionPair tabKap;
+ private KeyActionPair homeKap;
+ private KeyActionPair endKap;
+ private KeyActionPair pageUpKap;
+ private KeyActionPair pageDownKap;
+ private KeyActionPair ctrlCKap;
+
+ private KeyActionPair oldEscape, oldUp, oldDown, oldLeft, oldRight,
+ oldEnter, oldTab, oldHome, oldEnd, oldPageUp, oldPageDown,
+ oldCtrlC;
+
+ /**
+ * The space between the caret and the completion popup.
+ */
+ private static final int VERTICAL_SPACE = 1;
+
+ /**
+ * The class name of the Substance List UI.
+ */
+ private static final String SUBSTANCE_LIST_UI =
+ "org.pushingpixels.substance.internal.ui.SubstanceListUI";
+
+
+ protected AutoCompleteExtraRefreshComponent component;
+
+
+ /**
+ * Constructor.
+ *
+ * @param parent The parent window (hosting the text component).
+ * @param ac The auto-completion instance.
+ */
+ public AutoCompleteWithExtraRefreshPopupWindow(Window parent, final AutoCompletion ac) {
+
+ super(parent);
+ ComponentOrientation o = ac.getTextComponentOrientation();
+
+ this.ac = ac;
+ model = new CompletionListModel();
+ list = new PopupList(model);
+
+ list.setCellRenderer(new DelegatingCellRenderer());
+ list.addListSelectionListener(this);
+ list.addMouseListener(this);
+
+ JPanel contentPane = new JPanel(new BorderLayout());
+ JScrollPane sp = new JScrollPane(list,
+ JScrollPane.VERTICAL_SCROLLBAR_ALWAYS,
+ JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS);
+
+ // In 1.4, JScrollPane.setCorner() has a bug where it won't accept
+ // JScrollPane.LOWER_TRAILING_CORNER, even though that constant is
+ // defined. So we have to put the logic added in 1.5 to handle it
+ // here.
+ JPanel corner = new SizeGrip();
+ //sp.setCorner(JScrollPane.LOWER_TRAILING_CORNER, corner);
+ boolean isLeftToRight = o.isLeftToRight();
+ String str = isLeftToRight ? JScrollPane.LOWER_RIGHT_CORNER :
+ JScrollPane.LOWER_LEFT_CORNER;
+ sp.setCorner(str, corner);
+
+ contentPane.add(sp);
+ setContentPane(contentPane);
+ applyComponentOrientation(o);
+
+ // Give apps a chance to decorate us with drop shadows, etc.
+ if (Util.getShouldAllowDecoratingMainAutoCompleteWindows()) {
+ PopupWindowDecorator decorator = PopupWindowDecorator.get();
+ if (decorator != null) {
+ decorator.decorate(this);
+ }
+ }
+
+ pack();
+
+ setFocusableWindowState(false);
+
+ lastLine = -1;
+
+ }
+
+
+ public void caretUpdate(CaretEvent e) {
+ if (isVisible()) { // Should always be true
+ int line = ac.getLineOfCaret();
+ if (line != lastLine) {
+ lastLine = -1;
+ setVisible(false);
+ } else {
+ doAutocomplete();
+ }
+ } else if (AutoCompletion.isDebug()) {
+ Thread.dumpStack();
+ }
+ }
+
+
+ /**
+ * Creates the description window.
+ *
+ * @return The description window.
+ */
+ private AutoCompleteDescWindow createDescriptionWindow() {
+ AutoCompleteDescWindow dw = new AutoCompleteDescWindow(this, ac);
+ dw.applyComponentOrientation(ac.getTextComponentOrientation());
+ Dimension size = preferredDescWindowSize;
+ if (size == null) {
+ size = getSize();
+ }
+ dw.setSize(size);
+ return dw;
+ }
+
+
+ /**
+ * Creates the mappings from keys to Actions we'll be putting into the
+ * text component's ActionMap and InputMap.
+ */
+ private void createKeyActionPairs() {
+
+ // Actions we'll install.
+ EnterAction enterAction = new EnterAction();
+ escapeKap = new KeyActionPair("Escape", new EscapeAction());
+ upKap = new KeyActionPair("Up", new UpAction());
+ downKap = new KeyActionPair("Down", new DownAction());
+ leftKap = new KeyActionPair("Left", new LeftAction());
+ rightKap = new KeyActionPair("Right", new RightAction());
+ enterKap = new KeyActionPair("Enter", enterAction);
+ tabKap = new KeyActionPair("Tab", enterAction);
+ homeKap = new KeyActionPair("Home", new HomeAction());
+ endKap = new KeyActionPair("End", new EndAction());
+ pageUpKap = new KeyActionPair("PageUp", new PageUpAction());
+ pageDownKap = new KeyActionPair("PageDown", new PageDownAction());
+ ctrlCKap = new KeyActionPair("CtrlC", new CopyAction());
+
+ // Buffers for the actions we replace.
+ oldEscape = new KeyActionPair();
+ oldUp = new KeyActionPair();
+ oldDown = new KeyActionPair();
+ oldLeft = new KeyActionPair();
+ oldRight = new KeyActionPair();
+ oldEnter = new KeyActionPair();
+ oldTab = new KeyActionPair();
+ oldHome = new KeyActionPair();
+ oldEnd = new KeyActionPair();
+ oldPageUp = new KeyActionPair();
+ oldPageDown = new KeyActionPair();
+ oldCtrlC = new KeyActionPair();
+
+ }
+
+
+ protected void doAutocomplete() {
+ lastLine = ac.refreshPopupWindow();
+ }
+
+
+ /**
+ * Returns the copy keystroke to use for this platform.
+ *
+ * @return The copy keystroke.
+ */
+ private static final KeyStroke getCopyKeyStroke() {
+ int key = KeyEvent.VK_C;
+ int mask = Toolkit.getDefaultToolkit().getMenuShortcutKeyMask();
+ return KeyStroke.getKeyStroke(key, mask);
+ }
+
+
+ /**
+ * Returns the default list cell renderer used when a completion provider
+ * does not supply its own.
+ *
+ * @return The default list cell renderer.
+ * @see #setListCellRenderer(ListCellRenderer)
+ */
+ public ListCellRenderer getListCellRenderer() {
+ DelegatingCellRenderer dcr = (DelegatingCellRenderer) list.
+ getCellRenderer();
+ return dcr.getFallbackCellRenderer();
+ }
+
+
+ /**
+ * Returns the selected value, or null
if nothing is selected.
+ *
+ * @return The selected value.
+ */
+ public Completion getSelection() {
+ return isShowing() ? (Completion) list.getSelectedValue() : lastSelection;
+ }
+
+
+ /**
+ * Inserts the currently selected completion.
+ *
+ * @see #getSelection()
+ */
+ private void insertSelectedCompletion() {
+ Completion comp = getSelection();
+ ac.insertCompletion(comp);
+ }
+
+ public void installComp(AutoCompleteExtraRefreshComponent component){
+ this.component = component;
+ }
+
+ public void refreshInstallComp() {
+ component.refresh(getSelection().getReplacementText());
+ }
+
+ /**
+ * Registers keyboard actions to listen for in the text component and
+ * intercept.
+ *
+ * @see #uninstallKeyBindings()
+ */
+ private void installKeyBindings() {
+
+ if (AutoCompletion.isDebug()) {
+ FineLoggerFactory.getLogger().debug("PopupWindow: Installing keybindings");
+ }
+
+ if (escapeKap == null) { // Lazily create actions.
+ createKeyActionPairs();
+ }
+
+ JTextComponent comp = ac.getTextComponent();
+ InputMap im = comp.getInputMap();
+ ActionMap am = comp.getActionMap();
+
+ replaceAction(im, am, KeyEvent.VK_ESCAPE, escapeKap, oldEscape);
+ if (AutoCompletion.isDebug() && oldEscape.action == escapeKap.action) {
+ Thread.dumpStack();
+ }
+ replaceAction(im, am, KeyEvent.VK_UP, upKap, oldUp);
+ replaceAction(im, am, KeyEvent.VK_LEFT, leftKap, oldLeft);
+ replaceAction(im, am, KeyEvent.VK_DOWN, downKap, oldDown);
+ replaceAction(im, am, KeyEvent.VK_RIGHT, rightKap, oldRight);
+ replaceAction(im, am, KeyEvent.VK_ENTER, enterKap, oldEnter);
+ replaceAction(im, am, KeyEvent.VK_TAB, tabKap, oldTab);
+ replaceAction(im, am, KeyEvent.VK_HOME, homeKap, oldHome);
+ replaceAction(im, am, KeyEvent.VK_END, endKap, oldEnd);
+ replaceAction(im, am, KeyEvent.VK_PAGE_UP, pageUpKap, oldPageUp);
+ replaceAction(im, am, KeyEvent.VK_PAGE_DOWN, pageDownKap, oldPageDown);
+
+ // Make Ctrl+C copy from description window. This isn't done
+ // automagically because the desc. window is not focusable, and copying
+ // from text components can only be done from focused components.
+ KeyStroke ks = getCopyKeyStroke();
+ oldCtrlC.key = im.get(ks);
+ im.put(ks, ctrlCKap.key);
+ oldCtrlC.action = am.get(ctrlCKap.key);
+ am.put(ctrlCKap.key, ctrlCKap.action);
+
+ comp.addCaretListener(this);
+
+ }
+
+
+ public void mouseClicked(MouseEvent e) {
+ if (e.getClickCount() == 2 && e.getButton() == 1) {
+ insertSelectedCompletion();
+ }
+ }
+
+
+ public void mouseEntered(MouseEvent e) {
+ }
+
+
+ public void mouseExited(MouseEvent e) {
+ }
+
+
+ public void mousePressed(MouseEvent e) {
+ refreshInstallComp();
+ }
+
+
+ public void mouseReleased(MouseEvent e) {
+ }
+
+
+ /**
+ * Positions the description window relative to the completion choices
+ * window. We assume there is room on one side of the other for this
+ * entire window to fit.
+ */
+ private void positionDescWindow() {
+
+ boolean showDescWindow = descWindow != null && ac.isShowDescWindow();
+ if (!showDescWindow) {
+ return;
+ }
+
+ // Don't use getLocationOnScreen() as this throws an exception if
+ // window isn't visible yet, but getLocation() doesn't, and is in
+ // screen coordinates!
+ Point p = getLocation();
+ Rectangle screenBounds = Util.getScreenBoundsForPoint(p.x, p.y);
+ //Dimension screenSize = getToolkit().getScreenSize();
+ //int totalH = Math.max(getHeight(), descWindow.getHeight());
+
+ // Try to position to the right first (LTR)
+ int x;
+ if (ac.getTextComponentOrientation().isLeftToRight()) {
+ x = getX() + getWidth() + DIS;
+ if (x + descWindow.getWidth() > screenBounds.x + screenBounds.width) { // doesn't fit
+ x = getX() - DIS - descWindow.getWidth();
+ }
+ } else { // RTL
+ x = getX() - DIS - descWindow.getWidth();
+ if (x < screenBounds.x) { // Doesn't fit
+ x = getX() + getWidth() + DIS;
+ }
+ }
+
+ int y = getY();
+ if (aboveCaret) {
+ y = y + getHeight() - descWindow.getHeight();
+ }
+
+ if (x != descWindow.getX() || y != descWindow.getY()) {
+ descWindow.setLocation(x, y);
+ }
+
+ }
+
+
+ /**
+ * "Puts back" the original key/Action pair for a keystroke. This is used
+ * when this popup is hidden.
+ *
+ * @param im The input map.
+ * @param am The action map.
+ * @param key The keystroke whose key/Action pair to change.
+ * @param kap The (original) key/Action pair.
+ * @see #replaceAction(InputMap, ActionMap, int, KeyActionPair, KeyActionPair)
+ */
+ private void putBackAction(InputMap im, ActionMap am, int key,
+ KeyActionPair kap) {
+ KeyStroke ks = KeyStroke.getKeyStroke(key, 0);
+ am.put(im.get(ks), kap.action); // Original action for the "new" key
+ im.put(ks, kap.key); // Original key for the keystroke.
+ }
+
+
+ /**
+ * Replaces a key/Action pair in an InputMap and ActionMap with a new
+ * pair.
+ *
+ * @param im The input map.
+ * @param am The action map.
+ * @param key The keystroke whose information to replace.
+ * @param kap The new key/Action pair for key
.
+ * @param old A buffer in which to place the old key/Action pair.
+ * @see #putBackAction(InputMap, ActionMap, int, KeyActionPair)
+ */
+ private void replaceAction(InputMap im, ActionMap am, int key,
+ KeyActionPair kap, KeyActionPair old) {
+ KeyStroke ks = KeyStroke.getKeyStroke(key, 0);
+ old.key = im.get(ks);
+ im.put(ks, kap.key);
+ old.action = am.get(kap.key);
+ am.put(kap.key, kap.action);
+ }
+
+
+ /**
+ * Selects the first item in the completion list.
+ *
+ * @see #selectLastItem()
+ */
+ private void selectFirstItem() {
+ if (model.getSize() > 0) {
+ list.setSelectedIndex(0);
+ list.ensureIndexIsVisible(0);
+ }
+ }
+
+
+ /**
+ * Selects the last item in the completion list.
+ *
+ * @see #selectFirstItem()
+ */
+ private void selectLastItem() {
+ int index = model.getSize() - 1;
+ if (index > -1) {
+ list.setSelectedIndex(index);
+ list.ensureIndexIsVisible(index);
+ }
+ }
+
+
+ /**
+ * Selects the next item in the completion list.
+ *
+ * @see #selectPreviousItem()
+ */
+ private void selectNextItem() {
+ int index = list.getSelectedIndex();
+ if (index > -1) {
+ index = (index + 1) % model.getSize();
+ list.setSelectedIndex(index);
+ list.ensureIndexIsVisible(index);
+ }
+ }
+
+
+ /**
+ * Selects the completion item one "page down" from the currently selected
+ * one.
+ *
+ * @see #selectPageUpItem()
+ */
+ private void selectPageDownItem() {
+ int visibleRowCount = list.getVisibleRowCount();
+ int i = Math.min(list.getModel().getSize() - 1,
+ list.getSelectedIndex() + visibleRowCount);
+ list.setSelectedIndex(i);
+ list.ensureIndexIsVisible(i);
+ }
+
+
+ /**
+ * Selects the completion item one "page up" from the currently selected
+ * one.
+ *
+ * @see #selectPageDownItem()
+ */
+ private void selectPageUpItem() {
+ int visibleRowCount = list.getVisibleRowCount();
+ int i = Math.max(0, list.getSelectedIndex() - visibleRowCount);
+ list.setSelectedIndex(i);
+ list.ensureIndexIsVisible(i);
+ }
+
+
+ /**
+ * Selects the previous item in the completion list.
+ *
+ * @see #selectNextItem()
+ */
+ private void selectPreviousItem() {
+ int index = list.getSelectedIndex();
+ switch (index) {
+ case 0:
+ index = list.getModel().getSize() - 1;
+ break;
+ case -1: // Check for an empty list (would be an error)
+ index = list.getModel().getSize() - 1;
+ if (index == -1) {
+ return;
+ }
+ break;
+ default:
+ index = index - 1;
+ break;
+ }
+ list.setSelectedIndex(index);
+ list.ensureIndexIsVisible(index);
+ }
+
+
+ /**
+ * Sets the completions to display in the choices list. The first
+ * completion is selected.
+ *
+ * @param completions The completions to display.
+ */
+ public void setCompletions(List completions) {
+ model.setContents(completions);
+ selectFirstItem();
+ }
+
+
+ /**
+ * Sets the size of the description window.
+ *
+ * @param size The new size. This cannot be null
.
+ */
+ public void setDescriptionWindowSize(Dimension size) {
+ if (descWindow != null) {
+ descWindow.setSize(size);
+ } else {
+ preferredDescWindowSize = size;
+ }
+ }
+
+
+ /**
+ * Sets the default list cell renderer to use when a completion provider
+ * does not supply its own.
+ *
+ * @param renderer The renderer to use. If this is null
,
+ * a default renderer is used.
+ * @see #getListCellRenderer()
+ */
+ public void setListCellRenderer(ListCellRenderer renderer) {
+ DelegatingCellRenderer dcr = (DelegatingCellRenderer) list.
+ getCellRenderer();
+ dcr.setFallbackCellRenderer(renderer);
+ }
+
+
+ /**
+ * Sets the location of this window to be "good" relative to the specified
+ * rectangle. That rectangle should be the location of the text
+ * component's caret, in screen coordinates.
+ *
+ * @param r The text component's caret position, in screen coordinates.
+ */
+ public void setLocationRelativeTo(Rectangle r) {
+
+ // Multi-monitor support - make sure the completion window (and
+ // description window, if applicable) both fit in the same window in
+ // a multi-monitor environment. To do this, we decide which monitor
+ // the rectangle "r" is in, and use that one (just pick top-left corner
+ // as the defining point).
+ Rectangle screenBounds = Util.getScreenBoundsForPoint(r.x, r.y);
+ //Dimension screenSize = getToolkit().getScreenSize();
+
+ boolean showDescWindow = descWindow != null && ac.isShowDescWindow();
+ int totalH = getHeight();
+ if (showDescWindow) {
+ totalH = Math.max(totalH, descWindow.getHeight());
+ }
+
+ // Try putting our stuff "below" the caret first. We assume that the
+ // entire height of our stuff fits on the screen one way or the other.
+ aboveCaret = false;
+ int y = r.y + r.height + VERTICAL_SPACE;
+ if (y + totalH > screenBounds.height) {
+ y = r.y - VERTICAL_SPACE - getHeight();
+ aboveCaret = true;
+ }
+
+ // Get x-coordinate of completions. Try to align left edge with the
+ // caret first.
+ int x = r.x;
+ if (!ac.getTextComponentOrientation().isLeftToRight()) {
+ x -= getWidth(); // RTL => align right edge
+ }
+ if (x < screenBounds.x) {
+ x = screenBounds.x;
+ } else if (x + getWidth() > screenBounds.x + screenBounds.width) { // completions don't fit
+ x = screenBounds.x + screenBounds.width - getWidth();
+ }
+
+ setLocation(x, y);
+
+ // Position the description window, if necessary.
+ if (showDescWindow) {
+ positionDescWindow();
+ }
+
+ }
+
+
+ /**
+ * Toggles the visibility of this popup window.
+ *
+ * @param visible Whether this window should be visible.
+ */
+ @Override
+ public void setVisible(boolean visible) {
+
+ if (visible != isVisible()) {
+
+ if (visible) {
+ installKeyBindings();
+ lastLine = ac.getLineOfCaret();
+ selectFirstItem();
+ if (descWindow == null && ac.isShowDescWindow()) {
+ descWindow = createDescriptionWindow();
+ positionDescWindow();
+ }
+ // descWindow needs a kick-start the first time it's displayed.
+ // Also, the newly-selected item in the choices list is
+ // probably different from the previous one anyway.
+ if (descWindow != null) {
+ Completion c = (Completion) list.getSelectedValue();
+ if (c != null) {
+ descWindow.setDescriptionFor(c);
+ }
+ }
+ } else {
+ uninstallKeyBindings();
+ }
+
+ super.setVisible(visible);
+
+ // Some languages, such as Java, can use quite a lot of memory
+ // when displaying hundreds of completion choices. We pro-actively
+ // clear our list model here to make them available for GC.
+ // Otherwise, they stick around, and consider the following: a
+ // user starts code-completion for Java 5 SDK classes, then hides
+ // the dialog, then changes the "class path" to use a Java 6 SDK
+ // instead. On pressing Ctrl+space, a new array of Completions is
+ // created. If this window holds on to the previous Completions,
+ // you're getting roughly 2x the necessary Completions in memory
+ // until the Completions are actually passed to this window.
+ if (!visible) { // Do after super.setVisible(false)
+ lastSelection = (Completion) list.getSelectedValue();
+ model.clear();
+ }
+
+ // Must set descWindow's visibility one way or the other each time,
+ // because of the way child JWindows' visibility is handled - in
+ // some ways it's dependent on the parent, in other ways it's not.
+ if (descWindow != null) {
+ descWindow.setVisible(visible && ac.isShowDescWindow());
+ }
+
+ }
+
+ }
+
+
+ /**
+ * Stops intercepting certain keystrokes from the text component.
+ *
+ * @see #installKeyBindings()
+ */
+ private void uninstallKeyBindings() {
+
+ if (AutoCompletion.isDebug()) {
+ FineLoggerFactory.getLogger().debug("PopupWindow: Removing keybindings");
+ }
+
+ JTextComponent comp = ac.getTextComponent();
+ InputMap im = comp.getInputMap();
+ ActionMap am = comp.getActionMap();
+
+ putBackAction(im, am, KeyEvent.VK_ESCAPE, oldEscape);
+ putBackAction(im, am, KeyEvent.VK_UP, oldUp);
+ putBackAction(im, am, KeyEvent.VK_DOWN, oldDown);
+ putBackAction(im, am, KeyEvent.VK_LEFT, oldLeft);
+ putBackAction(im, am, KeyEvent.VK_RIGHT, oldRight);
+ putBackAction(im, am, KeyEvent.VK_ENTER, oldEnter);
+ putBackAction(im, am, KeyEvent.VK_TAB, oldTab);
+ putBackAction(im, am, KeyEvent.VK_HOME, oldHome);
+ putBackAction(im, am, KeyEvent.VK_END, oldEnd);
+ putBackAction(im, am, KeyEvent.VK_PAGE_UP, oldPageUp);
+ putBackAction(im, am, KeyEvent.VK_PAGE_DOWN, oldPageDown);
+
+ // Ctrl+C
+ KeyStroke ks = getCopyKeyStroke();
+ am.put(im.get(ks), oldCtrlC.action); // Original action
+ im.put(ks, oldCtrlC.key); // Original key
+
+ comp.removeCaretListener(this);
+
+ }
+
+
+ /**
+ * Updates the LookAndFeel of this window and the description
+ * window.
+ */
+ public void updateUI() {
+ SwingUtilities.updateComponentTreeUI(this);
+ if (descWindow != null) {
+ descWindow.updateUI();
+ }
+ }
+
+
+ /**
+ * Called when a new item is selected in the popup list.
+ *
+ * @param e The event.
+ */
+ public void valueChanged(ListSelectionEvent e) {
+ if (!e.getValueIsAdjusting()) {
+ Object value = list.getSelectedValue();
+ if (value != null && descWindow != null) {
+ descWindow.setDescriptionFor((Completion) value);
+ positionDescWindow();
+ }
+ }
+ }
+
+
+ class CopyAction extends AbstractAction {
+
+ public void actionPerformed(ActionEvent e) {
+ boolean doNormalCopy = false;
+ if (descWindow != null && descWindow.isVisible()) {
+ doNormalCopy = !descWindow.copy();
+ }
+ if (doNormalCopy) {
+ ac.getTextComponent().copy();
+ }
+ }
+
+ }
+
+
+ class DownAction extends AbstractAction {
+
+ public void actionPerformed(ActionEvent e) {
+ if (isVisible()) {
+ selectNextItem();
+ refreshInstallComp();
+ }
+ }
+
+ }
+
+
+ class EndAction extends AbstractAction {
+
+ public void actionPerformed(ActionEvent e) {
+ if (isVisible()) {
+ selectLastItem();
+ }
+ }
+
+ }
+
+
+ class EnterAction extends AbstractAction {
+
+ public void actionPerformed(ActionEvent e) {
+ if (isVisible()) {
+ insertSelectedCompletion();
+ refreshInstallComp();
+ }
+ }
+
+ }
+
+
+ class EscapeAction extends AbstractAction {
+
+ public void actionPerformed(ActionEvent e) {
+ if (isVisible()) {
+ setVisible(false);
+ }
+ }
+
+ }
+
+
+ class HomeAction extends AbstractAction {
+
+ public void actionPerformed(ActionEvent e) {
+ if (isVisible()) {
+ selectFirstItem();
+ }
+ }
+
+ }
+
+
+ /**
+ * A mapping from a key (an Object) to an Action.
+ */
+ private static class KeyActionPair {
+
+ public Object key;
+ public Action action;
+
+ public KeyActionPair() {
+ }
+
+ public KeyActionPair(Object key, Action a) {
+ this.key = key;
+ this.action = a;
+ }
+
+ }
+
+
+ class LeftAction extends AbstractAction {
+
+ public void actionPerformed(ActionEvent e) {
+ if (isVisible()) {
+ JTextComponent comp = ac.getTextComponent();
+ Caret c = comp.getCaret();
+ int dot = c.getDot();
+ if (dot > 0) {
+ c.setDot(--dot);
+ // Ensure moving left hasn't moved us up a line, thus
+ // hiding the popup window.
+ if (comp.isVisible()) {
+ if (lastLine != -1) {
+ doAutocomplete();
+ }
+ }
+ }
+ }
+ }
+
+ }
+
+
+ class PageDownAction extends AbstractAction {
+
+ public void actionPerformed(ActionEvent e) {
+ if (isVisible()) {
+ selectPageDownItem();
+ }
+ }
+
+ }
+
+
+ class PageUpAction extends AbstractAction {
+
+ public void actionPerformed(ActionEvent e) {
+ if (isVisible()) {
+ selectPageUpItem();
+ }
+ }
+
+ }
+
+
+ /**
+ * The actual list of completion choices in this popup window.
+ */
+ private class PopupList extends JList {
+
+ public PopupList(CompletionListModel model) {
+ super(model);
+ }
+
+ @Override
+ public void setUI(ListUI ui) {
+ if (Util.getUseSubstanceRenderers() &&
+ SUBSTANCE_LIST_UI.equals(ui.getClass().getName())) {
+ // Substance requires its special ListUI be installed for
+ // its renderers to actually render (!), but long completion
+ // lists (e.g. PHPCompletionProvider in RSTALanguageSupport)
+ // will simply populate too slowly on initial display (when
+ // calculating preferred size of all items), so in this case
+ // we give a prototype cell value.
+ CompletionProvider p = ac.getCompletionProvider();
+ BasicCompletion bc = new BasicCompletion(p, "Hello world");
+ setPrototypeCellValue(bc);
+ } else {
+ // Our custom UI that is faster for long HTML completion lists.
+ ui = new FastListUI();
+ setPrototypeCellValue(null);
+ }
+ super.setUI(ui);
+ }
+
+ }
+
+
+ class RightAction extends AbstractAction {
+
+ public void actionPerformed(ActionEvent e) {
+ if (isVisible()) {
+ JTextComponent comp = ac.getTextComponent();
+ Caret c = comp.getCaret();
+ int dot = c.getDot();
+ if (dot < comp.getDocument().getLength()) {
+ c.setDot(++dot);
+ // Ensure moving right hasn't moved us up a line, thus
+ // hiding the popup window.
+ if (comp.isVisible()) {
+ if (lastLine != -1) {
+ doAutocomplete();
+ }
+ }
+ }
+ }
+ }
+
+ }
+
+
+ class UpAction extends AbstractAction {
+
+ public void actionPerformed(ActionEvent e) {
+ if (isVisible()) {
+ selectPreviousItem();
+ refreshInstallComp();
+ }
+ }
+
+ }
+
+}
diff --git a/designer-base/src/main/java/com/fr/design/gui/autocomplete/AutoCompletionWithExtraRefresh.java b/designer-base/src/main/java/com/fr/design/gui/autocomplete/AutoCompletionWithExtraRefresh.java
new file mode 100644
index 0000000000..4def5370d0
--- /dev/null
+++ b/designer-base/src/main/java/com/fr/design/gui/autocomplete/AutoCompletionWithExtraRefresh.java
@@ -0,0 +1,1260 @@
+package com.fr.design.gui.autocomplete;
+
+import com.fr.design.formula.FormulaPane;
+import java.awt.ComponentOrientation;
+import java.awt.Dimension;
+import java.awt.Point;
+import java.awt.Rectangle;
+import java.awt.Window;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.ComponentAdapter;
+import java.awt.event.ComponentEvent;
+import java.awt.event.FocusAdapter;
+import java.awt.event.FocusEvent;
+import java.awt.event.HierarchyEvent;
+import java.awt.event.HierarchyListener;
+import java.awt.event.KeyEvent;
+import java.awt.event.WindowEvent;
+import java.awt.event.WindowFocusListener;
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+import java.util.List;
+import javax.swing.AbstractAction;
+import javax.swing.Action;
+import javax.swing.ActionMap;
+import javax.swing.InputMap;
+import javax.swing.JComponent;
+import javax.swing.KeyStroke;
+import javax.swing.ListCellRenderer;
+import javax.swing.SwingUtilities;
+import javax.swing.Timer;
+import javax.swing.UIManager;
+import javax.swing.event.CaretEvent;
+import javax.swing.event.CaretListener;
+import javax.swing.event.DocumentEvent;
+import javax.swing.event.DocumentListener;
+import javax.swing.text.BadLocationException;
+import javax.swing.text.Caret;
+import javax.swing.text.Document;
+import javax.swing.text.Element;
+import javax.swing.text.JTextComponent;
+
+
+import static com.fr.design.gui.syntax.ui.rtextarea.RTADefaultInputMap.DEFAULT_MODIFIER;
+
+public abstract class AutoCompletionWithExtraRefresh extends AutoCompletion{
+ private AutoCompleteExtraRefreshComponent area;
+
+ /**
+ * The text component we're providing completion for.
+ */
+ private JTextComponent textComponent;
+
+ /**
+ * The parent window of {@link #textComponent}.
+ */
+ private Window parentWindow;
+
+ /**
+ * The popup window containing completion choices.
+ */
+ private AutoCompleteWithExtraRefreshPopupWindow popupWindow;
+
+ /**
+ * The preferred size of the completion choices window. This field exists
+ * because the user will likely set the preferred size of the window
+ * before it is actually created.
+ */
+ private Dimension preferredChoicesWindowSize;
+
+ /**
+ * The preferred size of the optional description window. This field
+ * only exists because the user may (and usually will) set the size of
+ * the description window before it exists (it must be parented to a
+ * Window).
+ */
+ private Dimension preferredDescWindowSize;
+
+ /**
+ * Manages any parameterized completions that are inserted.
+ */
+ private ParameterizedCompletionContext pcc;
+
+ /**
+ * Provides the completion options relevant to the current caret position.
+ */
+ private CompletionProvider provider;
+
+ /**
+ * The renderer to use for the completion choices. If this is
+ * null
, then a default renderer is used.
+ */
+ private ListCellRenderer renderer;
+
+ /**
+ * The handler to use when an external URL is clicked in the help
+ * documentation.
+ */
+ private ExternalURLHandler externalURLHandler;
+
+ /**
+ * An optional redirector that converts URL's to some other location before
+ * being handed over to externalURLHandler
.
+ */
+ private static LinkRedirector linkRedirector;
+
+ /**
+ * Whether the description window should be displayed along with the
+ * completion choice window.
+ */
+ private boolean showDescWindow;
+
+ /**
+ * Whether auto-complete is enabled.
+ */
+ private boolean autoCompleteEnabled;
+
+ /**
+ * Whether the auto-activation of auto-complete (after a delay, after the
+ * user types an appropriate character) is enabled.
+ */
+ private boolean autoActivationEnabled;
+
+ /**
+ * Whether or not, when there is only a single auto-complete option
+ * that matches the text at the current text position, that text should
+ * be auto-inserted, instead of the completion window displaying.
+ */
+ private boolean autoCompleteSingleChoices;
+
+ /**
+ * Whether parameter assistance is enabled.
+ */
+ private boolean parameterAssistanceEnabled;
+
+ /**
+ * A renderer used for {@link Completion}s in the optional parameter
+ * choices popup window (displayed when a {@link ParameterizedCompletion}
+ * is code-completed). If this isn't set, a default renderer is used.
+ */
+ private ListCellRenderer paramChoicesRenderer;
+
+ /**
+ * The keystroke that triggers the completion window.
+ */
+ private KeyStroke trigger;
+
+ /**
+ * The previous key in the text component's InputMap
for the
+ * trigger key.
+ */
+ private Object oldTriggerKey;
+
+ /**
+ * The action previously assigned to {@link #trigger}, so we can reset it
+ * if the user disables auto-completion.
+ */
+ private Action oldTriggerAction;
+
+ /**
+ * The previous key in the text component's InputMap
for the
+ * parameter completion trigger key.
+ */
+ private Object oldParenKey;
+
+ /**
+ * The action previously assigned to the parameter completion key, so we
+ * can reset it when we uninstall.
+ */
+ private Action oldParenAction;
+
+ /**
+ * Listens for events in the parent window that affect the visibility of
+ * the popup windows.
+ */
+ private ParentWindowListener parentWindowListener;
+
+ /**
+ * Listens for events from the text component that affect the visibility
+ * of the popup windows.
+ */
+ private TextComponentListener textComponentListener;
+
+ /**
+ * Listens for events in the text component that cause the popup windows
+ * to automatically activate.
+ */
+ private AutoActivationListener autoActivationListener;
+
+ /**
+ * Listens for LAF changes so the auto-complete windows automatically
+ * update themselves accordingly.
+ */
+ private LookAndFeelChangeListener lafListener;
+
+ /**
+ * The key used in the input map for the AutoComplete action.
+ */
+ private static final String PARAM_TRIGGER_KEY = "AutoComplete";
+
+ /**
+ * Key used in the input map for the parameter completion action.
+ */
+ private static final String PARAM_COMPLETE_KEY = "AutoCompletion.FunctionStart";
+
+ /**
+ * Stores how to render auto-completion-specific highlights in text
+ * components.
+ */
+ private static final AutoCompletionStyleContext STYLE_CONTEXT =
+ new AutoCompletionStyleContext();
+
+ /**
+ * Whether debug messages should be printed to stdout as AutoCompletion
+ * runs.
+ */
+ private static final boolean DEBUG = initDebug();
+
+ Window getParentWindow(){
+ return parentWindow;
+ }
+
+ ListCellRenderer getCellRender(){
+ return renderer;
+ }
+
+ Dimension getPreferredChoicesWindowSize(){
+ return preferredChoicesWindowSize;
+ }
+
+ Dimension getPreferredDescWindowSize(){
+ return preferredDescWindowSize;
+ }
+ /**
+ * Constructor.
+ *
+ * @param provider The completion provider. This cannot be
+ * null
.
+ */
+ public AutoCompletionWithExtraRefresh(CompletionProvider provider) {
+ super(provider);
+ setChoicesWindowSize(350, 200);
+ setDescriptionWindowSize(350, 250);
+
+ setCompletionProvider(provider);
+ setTriggerKey(getDefaultTriggerKey());
+ setAutoCompleteEnabled(true);
+ setAutoCompleteSingleChoices(true);
+ setAutoActivationEnabled(false);
+ setShowDescWindow(false);
+ parentWindowListener = new ParentWindowListener();
+ textComponentListener = new TextComponentListener();
+ autoActivationListener = new AutoActivationListener();
+ lafListener = new LookAndFeelChangeListener();
+ }
+
+
+ /**
+ * Displays the popup window. Hosting applications can call this method
+ * to programmatically begin an auto-completion operation.
+ */
+ public void doCompletion() {
+ refreshPopupWindow();
+ }
+
+
+ /**
+ * Returns the delay between when the user types a character and when the
+ * code completion popup should automatically appear (if applicable).
+ *
+ * @return The delay, in milliseconds.
+ * @see #setAutoActivationDelay(int)
+ */
+ public int getAutoActivationDelay() {
+ return autoActivationListener.timer.getDelay();
+ }
+
+
+ /**
+ * Returns whether, if a single auto-complete choice is available, it
+ * should be automatically inserted, without displaying the popup menu.
+ *
+ * @return Whether to auto-complete single choices.
+ * @see #setAutoCompleteSingleChoices(boolean)
+ */
+ public boolean isAutoCompleteSingleChoices() {
+ return autoCompleteSingleChoices;
+ }
+
+
+ /**
+ * Returns the completion provider.
+ *
+ * @return The completion provider.
+ */
+ public CompletionProvider getCompletionProvider() {
+ return provider;
+ }
+
+
+ /**
+ * Returns whether debug is enabled for AutoCompletion.
+ *
+ * @return Whether debug is enabled.
+ */
+ static boolean isDebug() {
+ return DEBUG;
+ }
+
+
+ /**
+ * Returns the default auto-complete "trigger key" for this OS. For
+ * Windows, for example, it is Ctrl+Space.
+ *
+ * @return The default auto-complete trigger key.
+ */
+ public static KeyStroke getDefaultTriggerKey() {
+ // Default to CTRL, even on Mac, since Ctrl+Space activates Spotlight
+ return KeyStroke.getKeyStroke(KeyEvent.VK_SPACE, DEFAULT_MODIFIER);
+ }
+
+
+ /**
+ * Returns the handler to use when an external URL is clicked in the
+ * description window.
+ *
+ * @return The handler.
+ * @see #setExternalURLHandler(ExternalURLHandler)
+ * @see #getLinkRedirector()
+ */
+ public ExternalURLHandler getExternalURLHandler() {
+ return externalURLHandler;
+ }
+
+
+ int getLineOfCaret() {
+ Document doc = textComponent.getDocument();
+ Element root = doc.getDefaultRootElement();
+ return root.getElementIndex(textComponent.getCaretPosition());
+ }
+
+
+ /**
+ * Returns the link redirector, if any.
+ *
+ * @return The link redirector, or null
if none.
+ * @see #setLinkRedirector(LinkRedirector)
+ */
+ public static LinkRedirector getLinkRedirector() {
+ return linkRedirector;
+ }
+
+
+ /**
+ * Returns the default list cell renderer used when a completion provider
+ * does not supply its own.
+ *
+ * @return The default list cell renderer.
+ * @see #setListCellRenderer(ListCellRenderer)
+ */
+ public ListCellRenderer getListCellRenderer() {
+ return renderer;
+ }
+
+
+ /**
+ * Returns the renderer to use for {@link Completion}s in the optional
+ * parameter choices popup window (displayed when a
+ * {@link ParameterizedCompletion} is code-completed). If this returns
+ * null
, a default renderer is used.
+ *
+ * @return The renderer to use.
+ * @see #setParamChoicesRenderer(ListCellRenderer)
+ * @see #isParameterAssistanceEnabled()
+ */
+ public ListCellRenderer getParamChoicesRenderer() {
+ return paramChoicesRenderer;
+ }
+
+
+ /**
+ * Returns the text to replace with in the document. This is a
+ * "last-chance" hook for subclasses to make special modifications to the
+ * completion text inserted. The default implementation simply returns
+ * c.getReplacementText(). You usually will not need to modify
+ * this method.
+ *
+ * @param c The completion being inserted.
+ * @param doc The document being modified.
+ * @param start The start of the text being replaced.
+ * @param len The length of the text being replaced.
+ * @return The text to replace with.
+ */
+ protected String getReplacementText(Completion c, Document doc, int start,
+ int len) {
+ return c.getReplacementText();
+ }
+
+
+ /**
+ * Returns whether the "description window" should be shown alongside
+ * the completion window.
+ *
+ * @return Whether the description window should be shown.
+ * @see #setShowDescWindow(boolean)
+ */
+ public boolean isShowDescWindow() {
+ return showDescWindow;
+ }
+
+
+ /**
+ * Returns the style context describing how auto-completion related
+ * highlights in the editor are rendered.
+ *
+ * @return The style context.
+ */
+ public static AutoCompletionStyleContext getStyleContext() {
+ return STYLE_CONTEXT;
+ }
+
+
+ /**
+ * Returns the text component for which auto-completion is enabled.
+ *
+ * @return The text component, or null
if this
+ * {@link AutoCompletion} is not installed on any text component.
+ * @see #install(JTextComponent)
+ */
+ public JTextComponent getTextComponent() {
+ return textComponent;
+ }
+
+
+ /**
+ * Returns the orientation of the text component we're installed to.
+ *
+ * @return The orientation of the text component, or null
if
+ * we are not installed on one.
+ */
+ ComponentOrientation getTextComponentOrientation() {
+ return textComponent == null ? null :
+ textComponent.getComponentOrientation();
+ }
+
+
+ /**
+ * Returns the "trigger key" used for auto-complete.
+ *
+ * @return The trigger key.
+ * @see #setTriggerKey(KeyStroke)
+ */
+ public KeyStroke getTriggerKey() {
+ return trigger;
+ }
+
+
+ /**
+ * Hides any child windows being displayed by the auto-completion system.
+ *
+ * @return Whether any windows were visible.
+ */
+ public boolean hideChildWindows() {
+ //return hidePopupWindow() || hideToolTipWindow();
+ boolean res = hidePopupWindow();
+ res |= hideParameterCompletionPopups();
+ return res;
+ }
+
+
+ /**
+ * Hides and disposes of any parameter completion-related popups.
+ *
+ * @return Whether any such windows were visible (and thus hidden).
+ */
+ private boolean hideParameterCompletionPopups() {
+ if (pcc != null) {
+ pcc.deactivate();
+ pcc = null;
+ return true;
+ }
+ return false;
+ }
+
+
+ /**
+ * Hides the popup window, if it is visible.
+ *
+ * @return Whether the popup window was visible.
+ */
+ protected boolean hidePopupWindow() {
+ if (popupWindow != null) {
+ if (popupWindow.isVisible()) {
+ popupWindow.setVisible(false);
+ return true;
+ }
+ }
+ return false;
+ }
+
+
+ /**
+ * Determines whether debug should be enabled for the AutoCompletion
+ * library. This method checks a system property, but takes care of
+ * {@link SecurityException}s in case we're in an applet or WebStart.
+ *
+ * @return Whether debug should be enabled.
+ */
+ private static boolean initDebug() {
+ boolean debug = false;
+ try {
+ debug = Boolean.getBoolean("AutoCompletion.debug");
+ } catch (SecurityException se) { // We're in an applet or WebStart.
+ debug = false;
+ }
+ return debug;
+ }
+
+
+ /**
+ * Installs this auto-completion on a text component. If this
+ * {@link AutoCompletion} is already installed on another text component,
+ * it is uninstalled first.
+ *
+ * @param c The text component.
+ * @see #uninstall()
+ */
+ public void install(JTextComponent c) {
+
+ if (textComponent != null) {
+ uninstall();
+ }
+
+ this.textComponent = c;
+ installTriggerKey(getTriggerKey());
+
+ // Install the function completion key, if there is one.
+ // NOTE: We cannot do this if the start char is ' ' (e.g. just a space
+ // between the function name and parameters) because it overrides
+ // RSTA's special space action. It seems KeyStorke.getKeyStroke(' ')
+ // hoses ctrl+space, shift+space, etc., even though I think it
+ // shouldn't...
+ char start = provider.getParameterListStart();
+ if (start != 0 && start != ' ') {
+ InputMap im = c.getInputMap();
+ ActionMap am = c.getActionMap();
+ KeyStroke ks = KeyStroke.getKeyStroke(start);
+ oldParenKey = im.get(ks);
+ im.put(ks, PARAM_COMPLETE_KEY);
+ oldParenAction = am.get(PARAM_COMPLETE_KEY);
+ am.put(PARAM_COMPLETE_KEY,
+ new ParameterizedCompletionStartAction(start));
+ }
+
+ textComponentListener.addTo(this.textComponent);
+ // In case textComponent is already in a window...
+ textComponentListener.hierarchyChanged(null);
+
+ if (isAutoActivationEnabled()) {
+ autoActivationListener.addTo(this.textComponent);
+ }
+
+ UIManager.addPropertyChangeListener(lafListener);
+ updateUI(); // In case there have been changes since we uninstalled
+
+ }
+
+
+ /**
+ * Installs a "trigger key" action onto the current text component.
+ *
+ * @param ks The keystroke that should trigger the action.
+ */
+ private void installTriggerKey(KeyStroke ks) {
+ InputMap im = textComponent.getInputMap();
+ oldTriggerKey = im.get(ks);
+ im.put(ks, PARAM_TRIGGER_KEY);
+ ActionMap am = textComponent.getActionMap();
+ oldTriggerAction = am.get(PARAM_TRIGGER_KEY);
+ am.put(PARAM_TRIGGER_KEY, new AutoCompleteAction());
+ }
+
+
+ /**
+ * Returns whether auto-activation is enabled (that is, whether the
+ * completion popup will automatically appear after a delay when the user
+ * types an appropriate character). Note that this parameter will be
+ * ignored if auto-completion is disabled.
+ *
+ * @return Whether auto-activation is enabled.
+ * @see #setAutoActivationEnabled(boolean)
+ * @see #getAutoActivationDelay()
+ * @see #isAutoCompleteEnabled()
+ */
+ public boolean isAutoActivationEnabled() {
+ return autoActivationEnabled;
+ }
+
+
+ /**
+ * Returns whether auto-completion is enabled.
+ *
+ * @return Whether auto-completion is enabled.
+ * @see #setAutoCompleteEnabled(boolean)
+ */
+ public boolean isAutoCompleteEnabled() {
+ return autoCompleteEnabled;
+ }
+
+
+ /**
+ * Returns whether parameter assistance is enabled.
+ *
+ * @return Whether parameter assistance is enabled.
+ * @see #setParameterAssistanceEnabled(boolean)
+ */
+ public boolean isParameterAssistanceEnabled() {
+ return parameterAssistanceEnabled;
+ }
+
+
+ /**
+ * Returns whether the completion popup window is visible.
+ *
+ * @return Whether the completion popup window is visible.
+ */
+ public boolean isPopupVisible() {
+ return popupWindow != null && popupWindow.isVisible();
+ }
+
+ private boolean needSetPopupWindow(int count, int textLen) {
+ return (count == 1 && (isPopupVisible() || textLen == 0))
+ || (count == 1 && !isAutoCompleteSingleChoices())
+ || count > 1;
+ }
+
+ protected abstract AutoCompleteWithExtraRefreshPopupWindow createAutoCompletePopupWindow();
+
+ /**
+ * Sets the delay between when the user types a character and when the
+ * code completion popup should automatically appear (if applicable).
+ *
+ * @param ms The delay, in milliseconds. This should be greater than zero.
+ * @see #getAutoActivationDelay()
+ */
+ public void setAutoActivationDelay(int ms) {
+ ms = Math.max(0, ms);
+ autoActivationListener.timer.stop();
+ autoActivationListener.timer.setInitialDelay(ms);
+ }
+
+
+ /**
+ * Toggles whether auto-activation is enabled. Note that auto-activation
+ * also depends on auto-completion itself being enabled.
+ *
+ * @param enabled Whether auto-activation is enabled.
+ * @see #isAutoActivationEnabled()
+ * @see #setAutoActivationDelay(int)
+ */
+ public void setAutoActivationEnabled(boolean enabled) {
+ if (enabled != autoActivationEnabled) {
+ autoActivationEnabled = enabled;
+ if (textComponent != null) {
+ if (autoActivationEnabled) {
+ autoActivationListener.addTo(textComponent);
+ } else {
+ autoActivationListener.removeFrom(textComponent);
+ }
+ }
+ }
+ }
+
+
+ /**
+ * Sets whether auto-completion is enabled.
+ *
+ * @param enabled Whether auto-completion is enabled.
+ * @see #isAutoCompleteEnabled()
+ */
+ public void setAutoCompleteEnabled(boolean enabled) {
+ if (enabled != autoCompleteEnabled) {
+ autoCompleteEnabled = enabled;
+ hidePopupWindow();
+ }
+ }
+
+
+ /**
+ * Sets whether, if a single auto-complete choice is available, it should
+ * be automatically inserted, without displaying the popup menu.
+ *
+ * @param autoComplete Whether to auto-complete single choices.
+ * @see #isAutoCompleteSingleChoices()
+ */
+ public void setAutoCompleteSingleChoices(boolean autoComplete) {
+ autoCompleteSingleChoices = autoComplete;
+ }
+
+
+ /**
+ * Sets the completion provider being used.
+ *
+ * @param provider The new completion provider. This cannot be
+ * null
.
+ * @throws IllegalArgumentException If provider
is
+ * null
.
+ */
+ public void setCompletionProvider(CompletionProvider provider) {
+ if (provider == null) {
+ throw new IllegalArgumentException("provider cannot be null");
+ }
+ this.provider = provider;
+ hidePopupWindow(); // In case new choices should be displayed.
+ }
+
+
+ /**
+ * Sets the size of the completion choices window.
+ *
+ * @param w The new width.
+ * @param h The new height.
+ * @see #setDescriptionWindowSize(int, int)
+ */
+ public void setChoicesWindowSize(int w, int h) {
+ preferredChoicesWindowSize = new Dimension(w, h);
+ if (popupWindow != null) {
+ popupWindow.setSize(preferredChoicesWindowSize);
+ }
+ }
+
+
+ /**
+ * Sets the size of the description window.
+ *
+ * @param w The new width.
+ * @param h The new height.
+ * @see #setChoicesWindowSize(int, int)
+ */
+ public void setDescriptionWindowSize(int w, int h) {
+ preferredDescWindowSize = new Dimension(w, h);
+ if (popupWindow != null) {
+ popupWindow.setDescriptionWindowSize(preferredDescWindowSize);
+ }
+ }
+
+
+ /**
+ * Sets the handler to use when an external URL is clicked in the
+ * description window. This handler can perform some action, such as
+ * open the URL in a web browser. The default implementation will open
+ * the URL in a browser, but only if running in Java 6. If you want
+ * browser support for Java 5 and below, or otherwise want to respond to
+ * hyperlink clicks, you will have to install your own handler to do so.
+ *
+ * @param handler The new handler.
+ * @see #getExternalURLHandler()
+ */
+ public void setExternalURLHandler(ExternalURLHandler handler) {
+ this.externalURLHandler = handler;
+ }
+
+
+ /**
+ * Sets the redirector for external URL's found in code completion
+ * documentation. When a non-local link in completion popups is clicked,
+ * this redirector is given the chance to modify the URL fetched and
+ * displayed.
+ *
+ * @param redirector The link redirector, or null
for
+ * none.
+ * @see #getLinkRedirector()
+ */
+ public static void setLinkRedirector(LinkRedirector redirector) {
+ linkRedirector = redirector;
+ }
+
+
+ /**
+ * Sets the default list cell renderer to use when a completion provider
+ * does not supply its own.
+ *
+ * @param renderer The renderer to use. If this is null
,
+ * a default renderer is used.
+ * @see #getListCellRenderer()
+ */
+ public void setListCellRenderer(ListCellRenderer renderer) {
+ this.renderer = renderer;
+ if (popupWindow != null) {
+ popupWindow.setListCellRenderer(renderer);
+ hidePopupWindow();
+ }
+ }
+
+
+ /**
+ * Sets the renderer to use for {@link Completion}s in the optional
+ * parameter choices popup window (displayed when a
+ * {@link ParameterizedCompletion} is code-completed). If this isn't set,
+ * a default renderer is used.
+ *
+ * @param r The renderer to use.
+ * @see #getParamChoicesRenderer()
+ * @see #setParameterAssistanceEnabled(boolean)
+ */
+ public void setParamChoicesRenderer(ListCellRenderer r) {
+ paramChoicesRenderer = r;
+ }
+
+
+ /**
+ * Sets whether parameter assistance is enabled. If parameter assistance
+ * is enabled, and a "parameterized" completion (such as a function or
+ * method) is inserted, the user will get "assistance" in inserting the
+ * parameters in the form of a popup window with documentation and easy
+ * tabbing through the arguments (as seen in Eclipse and NetBeans).
+ *
+ * @param enabled Whether parameter assistance should be enabled.
+ * @see #isParameterAssistanceEnabled()
+ */
+ public void setParameterAssistanceEnabled(boolean enabled) {
+ parameterAssistanceEnabled = enabled;
+ }
+
+
+ /**
+ * Sets whether the "description window" should be shown beside the
+ * completion window.
+ *
+ * @param show Whether to show the description window.
+ * @see #isShowDescWindow()
+ */
+ public void setShowDescWindow(boolean show) {
+ hidePopupWindow(); // Needed to force it to take effect
+ showDescWindow = show;
+ }
+
+
+ /**
+ * Sets the keystroke that should be used to trigger the auto-complete
+ * popup window.
+ *
+ * @param ks The keystroke.
+ * @throws IllegalArgumentException If ks
is null
.
+ * @see #getTriggerKey()
+ */
+ public void setTriggerKey(KeyStroke ks) {
+ if (ks == null) {
+ throw new IllegalArgumentException("trigger key cannot be null");
+ }
+ if (!ks.equals(trigger)) {
+ if (textComponent != null) {
+ // Put old trigger action back.
+ uninstallTriggerKey();
+ // Grab current action for new trigger and replace it.
+ installTriggerKey(ks);
+ }
+ trigger = ks;
+ }
+ }
+
+
+ /**
+ * Uninstalls this auto-completion from its text component. If it is not
+ * installed on any text component, nothing happens.
+ *
+ * @see #install(JTextComponent)
+ */
+ public void uninstall() {
+
+ if (textComponent != null) {
+
+ hidePopupWindow(); // Unregisters listeners, actions, etc.
+
+ uninstallTriggerKey();
+
+ // Uninstall the function completion key.
+ char start = provider.getParameterListStart();
+ if (start != 0) {
+ KeyStroke ks = KeyStroke.getKeyStroke(start);
+ InputMap im = textComponent.getInputMap();
+ im.put(ks, oldParenKey);
+ ActionMap am = textComponent.getActionMap();
+ am.put(PARAM_COMPLETE_KEY, oldParenAction);
+ }
+
+ textComponentListener.removeFrom(textComponent);
+ if (parentWindow != null) {
+ parentWindowListener.removeFrom(parentWindow);
+ }
+
+ if (isAutoActivationEnabled()) {
+ autoActivationListener.removeFrom(textComponent);
+ }
+
+ UIManager.removePropertyChangeListener(lafListener);
+
+ textComponent = null;
+ popupWindow = null;
+
+ }
+
+ }
+
+
+ /**
+ * Replaces the "trigger key" action with the one that was there
+ * before auto-completion was installed.
+ */
+ private void uninstallTriggerKey() {
+ InputMap im = textComponent.getInputMap();
+ im.put(trigger, oldTriggerKey);
+ ActionMap am = textComponent.getActionMap();
+ am.put(PARAM_TRIGGER_KEY, oldTriggerAction);
+ }
+
+
+ /**
+ * Updates the LookAndFeel of the popup window. Applications can call
+ * this method as appropriate if they support changing the LookAndFeel
+ * at runtime.
+ */
+ private void updateUI() {
+ if (popupWindow != null) {
+ popupWindow.updateUI();
+ }
+ if (pcc != null) {
+ pcc.updateUI();
+ }
+ // Will practically always be a JComponent (a JLabel)
+ if (paramChoicesRenderer instanceof JComponent) {
+ ((JComponent) paramChoicesRenderer).updateUI();
+ }
+ }
+
+
+ /**
+ * Listens for events in the text component to auto-activate the code
+ * completion popup.
+ */
+ private class AutoActivationListener extends FocusAdapter
+ implements DocumentListener, CaretListener, ActionListener {
+
+ private Timer timer;
+ private boolean justInserted;
+
+ public AutoActivationListener() {
+ timer = new Timer(200, this);
+ timer.setRepeats(false);
+ }
+
+ public void actionPerformed(ActionEvent e) {
+ doCompletion();
+ }
+
+ public void addTo(JTextComponent tc) {
+ tc.addFocusListener(this);
+ tc.getDocument().addDocumentListener(this);
+ tc.addCaretListener(this);
+ }
+
+ public void caretUpdate(CaretEvent e) {
+ if (justInserted) {
+ justInserted = false;
+ } else {
+ timer.stop();
+ }
+ }
+
+ public void changedUpdate(DocumentEvent e) {
+ // Ignore
+ }
+
+ @Override
+ public void focusLost(FocusEvent e) {
+ timer.stop();
+ //hideChildWindows(); Other listener will do this
+ }
+
+ public void insertUpdate(DocumentEvent e) {
+ justInserted = false;
+ if (isAutoCompleteEnabled() && isAutoActivationEnabled() &&
+ e.getLength() == 1) {
+ if (provider.isAutoActivateOkay(textComponent)) {
+ timer.restart();
+ justInserted = true;
+ } else {
+ timer.stop();
+ }
+ } else {
+ timer.stop();
+ }
+ }
+
+ public void removeFrom(JTextComponent tc) {
+ tc.removeFocusListener(this);
+ tc.getDocument().removeDocumentListener(this);
+ tc.removeCaretListener(this);
+ timer.stop();
+ justInserted = false;
+ }
+
+ public void removeUpdate(DocumentEvent e) {
+ timer.stop();
+ }
+
+ }
+
+
+ /**
+ * The Action
that displays the popup window if
+ * auto-completion is enabled.
+ */
+ private class AutoCompleteAction extends AbstractAction {
+
+ public void actionPerformed(ActionEvent e) {
+ if (isAutoCompleteEnabled()) {
+ refreshPopupWindow();
+ } else if (oldTriggerAction != null) {
+ oldTriggerAction.actionPerformed(e);
+ }
+ }
+
+ }
+
+
+ /**
+ * Listens for LookAndFeel changes and updates the various popup windows
+ * involved in auto-completion accordingly.
+ */
+ private class LookAndFeelChangeListener implements PropertyChangeListener {
+
+ public void propertyChange(PropertyChangeEvent e) {
+ String name = e.getPropertyName();
+ if ("lookAndFeel".equals(name)) {
+ updateUI();
+ }
+ }
+
+ }
+
+
+ /**
+ * Action that starts a parameterized completion, e.g. after '(' is
+ * typed.
+ */
+ private class ParameterizedCompletionStartAction extends AbstractAction {
+
+ private String start;
+
+ public ParameterizedCompletionStartAction(char ch) {
+ this.start = Character.toString(ch);
+ }
+
+ public void actionPerformed(ActionEvent e) {
+
+ // Prevents keystrokes from messing up
+ boolean wasVisible = hidePopupWindow();
+
+ // Only proceed if they were selecting a completion
+ if (!wasVisible || !isParameterAssistanceEnabled()) {
+ textComponent.replaceSelection(start);
+ return;
+ }
+
+ Completion c = popupWindow.getSelection();
+ if (c instanceof ParameterizedCompletion) { // Should always be true
+ // Fixes capitalization of the entered text.
+ insertCompletion(c, true);
+ }
+
+ }
+
+ }
+
+
+ /**
+ * Listens for events in the parent window of the text component with
+ * auto-completion enabled.
+ */
+ private class ParentWindowListener extends ComponentAdapter
+ implements WindowFocusListener {
+
+ public void addTo(Window w) {
+ w.addComponentListener(this);
+ w.addWindowFocusListener(this);
+ }
+
+ @Override
+ public void componentHidden(ComponentEvent e) {
+ hideChildWindows();
+ }
+
+ @Override
+ public void componentMoved(ComponentEvent e) {
+ hideChildWindows();
+ }
+
+ @Override
+ public void componentResized(ComponentEvent e) {
+ hideChildWindows();
+ }
+
+ public void removeFrom(Window w) {
+ w.removeComponentListener(this);
+ w.removeWindowFocusListener(this);
+ }
+
+ public void windowGainedFocus(WindowEvent e) {
+ }
+
+ public void windowLostFocus(WindowEvent e) {
+ hideChildWindows();
+ }
+
+ }
+
+
+ /**
+ * Listens for events from the text component we're installed on.
+ */
+ private class TextComponentListener extends FocusAdapter
+ implements HierarchyListener {
+
+ void addTo(JTextComponent tc) {
+ tc.addFocusListener(this);
+ tc.addHierarchyListener(this);
+ }
+
+ /**
+ * Hide the auto-completion windows when the text component loses
+ * focus.
+ */
+ @Override
+ public void focusLost(FocusEvent e) {
+ hideChildWindows();
+ }
+
+ /**
+ * Called when the component hierarchy for our text component changes.
+ * When the text component is added to a new {@link Window}, this
+ * method registers listeners on that Window
.
+ *
+ * @param e The event.
+ */
+ public void hierarchyChanged(HierarchyEvent e) {
+
+ // NOTE: e many be null as we call this method at other times.
+ //System.out.println("Hierarchy changed! " + e);
+
+ Window oldParentWindow = parentWindow;
+ parentWindow = SwingUtilities.getWindowAncestor(textComponent);
+ if (parentWindow != oldParentWindow) {
+ if (oldParentWindow != null) {
+ parentWindowListener.removeFrom(oldParentWindow);
+ }
+ if (parentWindow != null) {
+ parentWindowListener.addTo(parentWindow);
+ }
+ }
+
+ }
+
+ public void removeFrom(JTextComponent tc) {
+ tc.removeFocusListener(this);
+ tc.removeHierarchyListener(this);
+ }
+
+ }
+
+ public void installExtraRefreshComponent(AutoCompleteExtraRefreshComponent jComp) {
+ area = jComp;
+ }
+
+
+ /**
+ * Displays a "tool tip" detailing the inputs to the function just entered.
+ *
+ * @param pc The completion.
+ * @param typedParamListStartChar Whether the parameterized completion list
+ * starting character was typed.
+ */
+ protected void startParameterizedCompletionAssistance(
+ ParameterizedCompletion pc, boolean typedParamListStartChar) {
+
+ // Get rid of the previous tool tip window, if there is one.
+ hideParameterCompletionPopups();
+
+ // Don't bother with a tool tip if there are no parameters, but if
+ // they typed e.g. the opening '(', make them overtype the ')'.
+ if (pc.getParamCount() == 0 && !(pc instanceof TemplateCompletion)) {
+ CompletionProvider p = pc.getProvider();
+ char end = p.getParameterListEnd(); // Might be '\0'
+ String text = end == '\0' ? "" : Character.toString(end);
+ if (typedParamListStartChar) {
+ String template = "${}" + text + "${cursor}";
+ textComponent.replaceSelection(Character.toString(p.getParameterListStart()));
+ TemplateCompletion tc = new TemplateCompletion(p, null, null, template);
+ pc = tc;
+ } else {
+ text = p.getParameterListStart() + text;
+ textComponent.replaceSelection(text);
+ return;
+ }
+ }
+
+ pcc = new ParameterizedCompletionContext(parentWindow, this, pc);
+ pcc.activate();
+
+ }
+
+ protected int refreshPopupWindow() {
+ // A return value of null => don't suggest completions
+ String text = provider.getAlreadyEnteredText(textComponent);
+ if (text == null && !isPopupVisible()) {
+ return getLineOfCaret();
+ }
+ // If the popup is currently visible, and they type a space (or any
+ // character that resets the completion list to "all completions"),
+ // the popup window should be hidden instead of being reset to show
+ // everything.
+ int textLen = text == null ? 0 : text.length();
+ if (textLen == 0 && isPopupVisible()) {
+ hidePopupWindow();
+ return getLineOfCaret();
+ }
+ final List completions = provider.getCompletions(textComponent);
+ int count = completions.size();
+ if (needSetPopupWindow(count, textLen)) {
+ if (popupWindow == null) {
+ popupWindow = createAutoCompletePopupWindow();
+ }
+ popupWindow.installComp(area);
+ popupWindow.setCompletions(completions);
+ if (!popupWindow.isVisible()) {
+ Rectangle r = null;
+ try {
+ r = textComponent.modelToView(textComponent.getCaretPosition());
+ } catch (BadLocationException ble) {
+ return -1;
+ }
+ Point p = new Point(r.x, r.y);
+ SwingUtilities.convertPointToScreen(p, textComponent);
+ r.x = p.x;
+ r.y = p.y;
+ popupWindow.setLocationRelativeTo(r);
+ popupWindow.setVisible(true);
+ }
+ } else if (count == 1) { // !isPopupVisible && autoCompleteSingleChoices
+ SwingUtilities.invokeLater(new Runnable() {
+ public void run() {
+ insertCompletion(completions.get(0));
+ }
+ });
+ } else {
+ hidePopupWindow();
+ }
+ return getLineOfCaret();
+ }
+}
diff --git a/designer-base/src/main/java/com/fr/design/gui/autocomplete/FormulaAutoCompletePopupWindow.java b/designer-base/src/main/java/com/fr/design/gui/autocomplete/FormulaAutoCompletePopupWindow.java
index 0e1c7c6ac2..daed7cd4c5 100644
--- a/designer-base/src/main/java/com/fr/design/gui/autocomplete/FormulaAutoCompletePopupWindow.java
+++ b/designer-base/src/main/java/com/fr/design/gui/autocomplete/FormulaAutoCompletePopupWindow.java
@@ -9,41 +9,7 @@
*/
package com.fr.design.gui.autocomplete;
-import com.fr.design.formula.FormulaPane;
-import com.fr.design.gui.syntax.ui.rsyntaxtextarea.PopupWindowDecorator;
-import com.fr.log.FineLoggerFactory;
-
-import javax.swing.AbstractAction;
-import javax.swing.Action;
-import javax.swing.ActionMap;
-import javax.swing.InputMap;
-import javax.swing.JList;
-import javax.swing.JPanel;
-import javax.swing.JScrollPane;
-import javax.swing.JWindow;
-import javax.swing.KeyStroke;
-import javax.swing.ListCellRenderer;
-import javax.swing.SwingUtilities;
-import javax.swing.event.CaretEvent;
-import javax.swing.event.CaretListener;
-import javax.swing.event.ListSelectionEvent;
-import javax.swing.event.ListSelectionListener;
-import javax.swing.plaf.ListUI;
-import javax.swing.text.Caret;
-import javax.swing.text.JTextComponent;
-import java.awt.BorderLayout;
-import java.awt.ComponentOrientation;
-import java.awt.Dimension;
-import java.awt.Point;
-import java.awt.Rectangle;
-import java.awt.Toolkit;
import java.awt.Window;
-import java.awt.event.ActionEvent;
-import java.awt.event.KeyEvent;
-import java.awt.event.MouseEvent;
-import java.awt.event.MouseListener;
-import java.util.List;
-
/**
* The actual popup window of choices. When visible, this window intercepts
@@ -55,949 +21,11 @@ import java.util.List;
* @author Robert Futrell
* @version 1.0
*/
-class FormulaAutoCompletePopupWindow extends JWindow implements CaretListener,
- ListSelectionListener, MouseListener {
-
- private final static int DIS = 5;
- /**
- * The parent AutoCompletion instance.
- */
- private FormulaPaneAutoCompletion ac;
-
- /**
- * The list of completion choices.
- */
- private JList list;
-
- /**
- * The contents of {@link #list()}.
- */
- private CompletionListModel model;
-
- /**
- * A hack to work around the fact that we clear our completion model (and
- * our selection) when hiding the completion window. This allows us to
- * still know what the user selected after the popup is hidden.
- */
- private Completion lastSelection;
-
- /**
- * Optional popup window containing a description of the currently
- * selected completion.
- */
- private AutoCompleteDescWindow descWindow;
-
- /**
- * The preferred size of the optional description window. This field
- * only exists because the user may (and usually will) set the size of
- * the description window before it exists (it must be parented to a
- * Window).
- */
- private Dimension preferredDescWindowSize;
-
- private FormulaPane.VariableTreeAndDescriptionArea component;
-
- /**
- * Whether the completion window and the optional description window
- * should be displayed above the current caret position (as opposed to
- * underneath it, which is preferred unless there is not enough space).
- */
- private boolean aboveCaret;
-
- private int lastLine;
-
- private KeyActionPair escapeKap;
- private KeyActionPair upKap;
- private KeyActionPair downKap;
- private KeyActionPair leftKap;
- private KeyActionPair rightKap;
- private KeyActionPair enterKap;
- private KeyActionPair tabKap;
- private KeyActionPair homeKap;
- private KeyActionPair endKap;
- private KeyActionPair pageUpKap;
- private KeyActionPair pageDownKap;
- private KeyActionPair ctrlCKap;
-
- private KeyActionPair oldEscape, oldUp, oldDown, oldLeft, oldRight,
- oldEnter, oldTab, oldHome, oldEnd, oldPageUp, oldPageDown,
- oldCtrlC;
-
- /**
- * The space between the caret and the completion popup.
- */
- private static final int VERTICAL_SPACE = 1;
-
- /**
- * The class name of the Substance List UI.
- */
- private static final String SUBSTANCE_LIST_UI =
- "org.pushingpixels.substance.internal.ui.SubstanceListUI";
+class FormulaAutoCompletePopupWindow extends AutoCompleteWithExtraRefreshPopupWindow {
- /**
- * Constructor.
- *
- * @param parent The parent window (hosting the text component).
- * @param ac The auto-completion instance.
- */
public FormulaAutoCompletePopupWindow(Window parent, final FormulaPaneAutoCompletion ac) {
-
- super(parent);
- ComponentOrientation o = ac.getTextComponentOrientation();
-
- this.ac = ac;
- model = new CompletionListModel();
- list = new PopupList(model);
-
- list.setCellRenderer(new DelegatingCellRenderer());
- list.addListSelectionListener(this);
- list.addMouseListener(this);
-
- JPanel contentPane = new JPanel(new BorderLayout());
- JScrollPane sp = new JScrollPane(list,
- JScrollPane.VERTICAL_SCROLLBAR_ALWAYS,
- JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS);
-
- // In 1.4, JScrollPane.setCorner() has a bug where it won't accept
- // JScrollPane.LOWER_TRAILING_CORNER, even though that constant is
- // defined. So we have to put the logic added in 1.5 to handle it
- // here.
- JPanel corner = new SizeGrip();
- //sp.setCorner(JScrollPane.LOWER_TRAILING_CORNER, corner);
- boolean isLeftToRight = o.isLeftToRight();
- String str = isLeftToRight ? JScrollPane.LOWER_RIGHT_CORNER :
- JScrollPane.LOWER_LEFT_CORNER;
- sp.setCorner(str, corner);
-
- contentPane.add(sp);
- setContentPane(contentPane);
- applyComponentOrientation(o);
-
- // Give apps a chance to decorate us with drop shadows, etc.
- if (Util.getShouldAllowDecoratingMainAutoCompleteWindows()) {
- PopupWindowDecorator decorator = PopupWindowDecorator.get();
- if (decorator != null) {
- decorator.decorate(this);
- }
- }
-
- pack();
-
- setFocusableWindowState(false);
-
- lastLine = -1;
-
- }
-
-
- public void caretUpdate(CaretEvent e) {
- if (isVisible()) { // Should always be true
- int line = ac.getLineOfCaret();
- if (line != lastLine) {
- lastLine = -1;
- setVisible(false);
- } else {
- doAutocomplete();
- }
- } else if (AutoCompletion.isDebug()) {
- Thread.dumpStack();
- }
- }
-
-
- /**
- * Creates the description window.
- *
- * @return The description window.
- */
- private AutoCompleteDescWindow createDescriptionWindow() {
- AutoCompleteDescWindow dw = new AutoCompleteDescWindow(this, ac);
- dw.applyComponentOrientation(ac.getTextComponentOrientation());
- Dimension size = preferredDescWindowSize;
- if (size == null) {
- size = getSize();
- }
- dw.setSize(size);
- return dw;
- }
-
-
- /**
- * Creates the mappings from keys to Actions we'll be putting into the
- * text component's ActionMap and InputMap.
- */
- private void createKeyActionPairs() {
-
- // Actions we'll install.
- EnterAction enterAction = new EnterAction();
- escapeKap = new KeyActionPair("Escape", new EscapeAction());
- upKap = new KeyActionPair("Up", new UpAction());
- downKap = new KeyActionPair("Down", new DownAction());
- leftKap = new KeyActionPair("Left", new LeftAction());
- rightKap = new KeyActionPair("Right", new RightAction());
- enterKap = new KeyActionPair("Enter", enterAction);
- tabKap = new KeyActionPair("Tab", enterAction);
- homeKap = new KeyActionPair("Home", new HomeAction());
- endKap = new KeyActionPair("End", new EndAction());
- pageUpKap = new KeyActionPair("PageUp", new PageUpAction());
- pageDownKap = new KeyActionPair("PageDown", new PageDownAction());
- ctrlCKap = new KeyActionPair("CtrlC", new CopyAction());
-
- // Buffers for the actions we replace.
- oldEscape = new KeyActionPair();
- oldUp = new KeyActionPair();
- oldDown = new KeyActionPair();
- oldLeft = new KeyActionPair();
- oldRight = new KeyActionPair();
- oldEnter = new KeyActionPair();
- oldTab = new KeyActionPair();
- oldHome = new KeyActionPair();
- oldEnd = new KeyActionPair();
- oldPageUp = new KeyActionPair();
- oldPageDown = new KeyActionPair();
- oldCtrlC = new KeyActionPair();
-
- }
-
-
- protected void doAutocomplete() {
- lastLine = ac.refreshPopupWindow();
- }
-
-
- /**
- * Returns the copy keystroke to use for this platform.
- *
- * @return The copy keystroke.
- */
- private static final KeyStroke getCopyKeyStroke() {
- int key = KeyEvent.VK_C;
- int mask = Toolkit.getDefaultToolkit().getMenuShortcutKeyMask();
- return KeyStroke.getKeyStroke(key, mask);
- }
-
-
- /**
- * Returns the default list cell renderer used when a completion provider
- * does not supply its own.
- *
- * @return The default list cell renderer.
- * @see #setListCellRenderer(ListCellRenderer)
- */
- public ListCellRenderer getListCellRenderer() {
- DelegatingCellRenderer dcr = (DelegatingCellRenderer) list.
- getCellRenderer();
- return dcr.getFallbackCellRenderer();
- }
-
-
- /**
- * Returns the selected value, or null
if nothing is selected.
- *
- * @return The selected value.
- */
- public Completion getSelection() {
- return isShowing() ? (Completion) list.getSelectedValue() : lastSelection;
- }
-
-
- /**
- * Inserts the currently selected completion.
- *
- * @see #getSelection()
- */
- private void insertSelectedCompletion() {
- Completion comp = getSelection();
- ac.insertCompletion(comp);
- }
-
- public void installComp(FormulaPane.VariableTreeAndDescriptionArea component) {
- this.component = component;
- }
-
- private void refreshInstallComp() {
- component.refreshText(getSelection().getReplacementText());
- }
-
-
- /**
- * Registers keyboard actions to listen for in the text component and
- * intercept.
- *
- * @see #uninstallKeyBindings()
- */
- private void installKeyBindings() {
-
- if (AutoCompletion.isDebug()) {
- FineLoggerFactory.getLogger().debug("PopupWindow: Installing keybindings");
- }
-
- if (escapeKap == null) { // Lazily create actions.
- createKeyActionPairs();
- }
-
- JTextComponent comp = ac.getTextComponent();
- InputMap im = comp.getInputMap();
- ActionMap am = comp.getActionMap();
-
- replaceAction(im, am, KeyEvent.VK_ESCAPE, escapeKap, oldEscape);
- if (AutoCompletion.isDebug() && oldEscape.action == escapeKap.action) {
- Thread.dumpStack();
- }
- replaceAction(im, am, KeyEvent.VK_UP, upKap, oldUp);
- replaceAction(im, am, KeyEvent.VK_LEFT, leftKap, oldLeft);
- replaceAction(im, am, KeyEvent.VK_DOWN, downKap, oldDown);
- replaceAction(im, am, KeyEvent.VK_RIGHT, rightKap, oldRight);
- replaceAction(im, am, KeyEvent.VK_ENTER, enterKap, oldEnter);
- replaceAction(im, am, KeyEvent.VK_TAB, tabKap, oldTab);
- replaceAction(im, am, KeyEvent.VK_HOME, homeKap, oldHome);
- replaceAction(im, am, KeyEvent.VK_END, endKap, oldEnd);
- replaceAction(im, am, KeyEvent.VK_PAGE_UP, pageUpKap, oldPageUp);
- replaceAction(im, am, KeyEvent.VK_PAGE_DOWN, pageDownKap, oldPageDown);
-
- // Make Ctrl+C copy from description window. This isn't done
- // automagically because the desc. window is not focusable, and copying
- // from text components can only be done from focused components.
- KeyStroke ks = getCopyKeyStroke();
- oldCtrlC.key = im.get(ks);
- im.put(ks, ctrlCKap.key);
- oldCtrlC.action = am.get(ctrlCKap.key);
- am.put(ctrlCKap.key, ctrlCKap.action);
-
- comp.addCaretListener(this);
-
- }
-
-
- public void mouseClicked(MouseEvent e) {
- if (e.getClickCount() == 2 && e.getButton() == 1) {
- insertSelectedCompletion();
- }
- }
-
-
- public void mouseEntered(MouseEvent e) {
- }
-
-
- public void mouseExited(MouseEvent e) {
- }
-
-
- public void mousePressed(MouseEvent e) {
- refreshInstallComp();
- }
-
-
- public void mouseReleased(MouseEvent e) {
- }
-
-
- /**
- * Positions the description window relative to the completion choices
- * window. We assume there is room on one side of the other for this
- * entire window to fit.
- */
- private void positionDescWindow() {
-
- boolean showDescWindow = descWindow != null && ac.isShowDescWindow();
- if (!showDescWindow) {
- return;
- }
-
- // Don't use getLocationOnScreen() as this throws an exception if
- // window isn't visible yet, but getLocation() doesn't, and is in
- // screen coordinates!
- Point p = getLocation();
- Rectangle screenBounds = Util.getScreenBoundsForPoint(p.x, p.y);
- //Dimension screenSize = getToolkit().getScreenSize();
- //int totalH = Math.max(getHeight(), descWindow.getHeight());
-
- // Try to position to the right first (LTR)
- int x;
- if (ac.getTextComponentOrientation().isLeftToRight()) {
- x = getX() + getWidth() + DIS;
- if (x + descWindow.getWidth() > screenBounds.x + screenBounds.width) { // doesn't fit
- x = getX() - DIS - descWindow.getWidth();
- }
- } else { // RTL
- x = getX() - DIS - descWindow.getWidth();
- if (x < screenBounds.x) { // Doesn't fit
- x = getX() + getWidth() + DIS;
- }
- }
-
- int y = getY();
- if (aboveCaret) {
- y = y + getHeight() - descWindow.getHeight();
- }
-
- if (x != descWindow.getX() || y != descWindow.getY()) {
- descWindow.setLocation(x, y);
- }
-
- }
-
-
- /**
- * "Puts back" the original key/Action pair for a keystroke. This is used
- * when this popup is hidden.
- *
- * @param im The input map.
- * @param am The action map.
- * @param key The keystroke whose key/Action pair to change.
- * @param kap The (original) key/Action pair.
- * @see #replaceAction(InputMap, ActionMap, int, KeyActionPair, KeyActionPair)
- */
- private void putBackAction(InputMap im, ActionMap am, int key,
- KeyActionPair kap) {
- KeyStroke ks = KeyStroke.getKeyStroke(key, 0);
- am.put(im.get(ks), kap.action); // Original action for the "new" key
- im.put(ks, kap.key); // Original key for the keystroke.
- }
-
-
- /**
- * Replaces a key/Action pair in an InputMap and ActionMap with a new
- * pair.
- *
- * @param im The input map.
- * @param am The action map.
- * @param key The keystroke whose information to replace.
- * @param kap The new key/Action pair for key
.
- * @param old A buffer in which to place the old key/Action pair.
- * @see #putBackAction(InputMap, ActionMap, int, KeyActionPair)
- */
- private void replaceAction(InputMap im, ActionMap am, int key,
- KeyActionPair kap, KeyActionPair old) {
- KeyStroke ks = KeyStroke.getKeyStroke(key, 0);
- old.key = im.get(ks);
- im.put(ks, kap.key);
- old.action = am.get(kap.key);
- am.put(kap.key, kap.action);
- }
-
-
- /**
- * Selects the first item in the completion list.
- *
- * @see #selectLastItem()
- */
- private void selectFirstItem() {
- if (model.getSize() > 0) {
- list.setSelectedIndex(0);
- list.ensureIndexIsVisible(0);
- }
- }
-
-
- /**
- * Selects the last item in the completion list.
- *
- * @see #selectFirstItem()
- */
- private void selectLastItem() {
- int index = model.getSize() - 1;
- if (index > -1) {
- list.setSelectedIndex(index);
- list.ensureIndexIsVisible(index);
- }
- }
-
-
- /**
- * Selects the next item in the completion list.
- *
- * @see #selectPreviousItem()
- */
- private void selectNextItem() {
- int index = list.getSelectedIndex();
- if (index > -1) {
- index = (index + 1) % model.getSize();
- list.setSelectedIndex(index);
- list.ensureIndexIsVisible(index);
- }
- }
-
-
- /**
- * Selects the completion item one "page down" from the currently selected
- * one.
- *
- * @see #selectPageUpItem()
- */
- private void selectPageDownItem() {
- int visibleRowCount = list.getVisibleRowCount();
- int i = Math.min(list.getModel().getSize() - 1,
- list.getSelectedIndex() + visibleRowCount);
- list.setSelectedIndex(i);
- list.ensureIndexIsVisible(i);
- }
-
-
- /**
- * Selects the completion item one "page up" from the currently selected
- * one.
- *
- * @see #selectPageDownItem()
- */
- private void selectPageUpItem() {
- int visibleRowCount = list.getVisibleRowCount();
- int i = Math.max(0, list.getSelectedIndex() - visibleRowCount);
- list.setSelectedIndex(i);
- list.ensureIndexIsVisible(i);
- }
-
-
- /**
- * Selects the previous item in the completion list.
- *
- * @see #selectNextItem()
- */
- private void selectPreviousItem() {
- int index = list.getSelectedIndex();
- switch (index) {
- case 0:
- index = list.getModel().getSize() - 1;
- break;
- case -1: // Check for an empty list (would be an error)
- index = list.getModel().getSize() - 1;
- if (index == -1) {
- return;
- }
- break;
- default:
- index = index - 1;
- break;
- }
- list.setSelectedIndex(index);
- list.ensureIndexIsVisible(index);
- }
-
-
- /**
- * Sets the completions to display in the choices list. The first
- * completion is selected.
- *
- * @param completions The completions to display.
- */
- public void setCompletions(List completions) {
- model.setContents(completions);
- selectFirstItem();
- }
-
-
- /**
- * Sets the size of the description window.
- *
- * @param size The new size. This cannot be null
.
- */
- public void setDescriptionWindowSize(Dimension size) {
- if (descWindow != null) {
- descWindow.setSize(size);
- } else {
- preferredDescWindowSize = size;
- }
- }
-
-
- /**
- * Sets the default list cell renderer to use when a completion provider
- * does not supply its own.
- *
- * @param renderer The renderer to use. If this is null
,
- * a default renderer is used.
- * @see #getListCellRenderer()
- */
- public void setListCellRenderer(ListCellRenderer renderer) {
- DelegatingCellRenderer dcr = (DelegatingCellRenderer) list.
- getCellRenderer();
- dcr.setFallbackCellRenderer(renderer);
- }
-
-
- /**
- * Sets the location of this window to be "good" relative to the specified
- * rectangle. That rectangle should be the location of the text
- * component's caret, in screen coordinates.
- *
- * @param r The text component's caret position, in screen coordinates.
- */
- public void setLocationRelativeTo(Rectangle r) {
-
- // Multi-monitor support - make sure the completion window (and
- // description window, if applicable) both fit in the same window in
- // a multi-monitor environment. To do this, we decide which monitor
- // the rectangle "r" is in, and use that one (just pick top-left corner
- // as the defining point).
- Rectangle screenBounds = Util.getScreenBoundsForPoint(r.x, r.y);
- //Dimension screenSize = getToolkit().getScreenSize();
-
- boolean showDescWindow = descWindow != null && ac.isShowDescWindow();
- int totalH = getHeight();
- if (showDescWindow) {
- totalH = Math.max(totalH, descWindow.getHeight());
- }
-
- // Try putting our stuff "below" the caret first. We assume that the
- // entire height of our stuff fits on the screen one way or the other.
- aboveCaret = false;
- int y = r.y + r.height + VERTICAL_SPACE;
- if (y + totalH > screenBounds.height) {
- y = r.y - VERTICAL_SPACE - getHeight();
- aboveCaret = true;
- }
-
- // Get x-coordinate of completions. Try to align left edge with the
- // caret first.
- int x = r.x;
- if (!ac.getTextComponentOrientation().isLeftToRight()) {
- x -= getWidth(); // RTL => align right edge
- }
- if (x < screenBounds.x) {
- x = screenBounds.x;
- } else if (x + getWidth() > screenBounds.x + screenBounds.width) { // completions don't fit
- x = screenBounds.x + screenBounds.width - getWidth();
- }
-
- setLocation(x, y);
-
- // Position the description window, if necessary.
- if (showDescWindow) {
- positionDescWindow();
- }
-
- }
-
-
- /**
- * Toggles the visibility of this popup window.
- *
- * @param visible Whether this window should be visible.
- */
- @Override
- public void setVisible(boolean visible) {
-
- if (visible != isVisible()) {
-
- if (visible) {
- installKeyBindings();
- lastLine = ac.getLineOfCaret();
- selectFirstItem();
- if (descWindow == null && ac.isShowDescWindow()) {
- descWindow = createDescriptionWindow();
- positionDescWindow();
- }
- // descWindow needs a kick-start the first time it's displayed.
- // Also, the newly-selected item in the choices list is
- // probably different from the previous one anyway.
- if (descWindow != null) {
- Completion c = (Completion) list.getSelectedValue();
- if (c != null) {
- descWindow.setDescriptionFor(c);
- }
- }
- } else {
- uninstallKeyBindings();
- }
-
- super.setVisible(visible);
-
- // Some languages, such as Java, can use quite a lot of memory
- // when displaying hundreds of completion choices. We pro-actively
- // clear our list model here to make them available for GC.
- // Otherwise, they stick around, and consider the following: a
- // user starts code-completion for Java 5 SDK classes, then hides
- // the dialog, then changes the "class path" to use a Java 6 SDK
- // instead. On pressing Ctrl+space, a new array of Completions is
- // created. If this window holds on to the previous Completions,
- // you're getting roughly 2x the necessary Completions in memory
- // until the Completions are actually passed to this window.
- if (!visible) { // Do after super.setVisible(false)
- lastSelection = (Completion) list.getSelectedValue();
- model.clear();
- }
-
- // Must set descWindow's visibility one way or the other each time,
- // because of the way child JWindows' visibility is handled - in
- // some ways it's dependent on the parent, in other ways it's not.
- if (descWindow != null) {
- descWindow.setVisible(visible && ac.isShowDescWindow());
- }
-
- }
-
- }
-
-
- /**
- * Stops intercepting certain keystrokes from the text component.
- *
- * @see #installKeyBindings()
- */
- private void uninstallKeyBindings() {
-
- if (AutoCompletion.isDebug()) {
- FineLoggerFactory.getLogger().debug("PopupWindow: Removing keybindings");
- }
-
- JTextComponent comp = ac.getTextComponent();
- InputMap im = comp.getInputMap();
- ActionMap am = comp.getActionMap();
-
- putBackAction(im, am, KeyEvent.VK_ESCAPE, oldEscape);
- putBackAction(im, am, KeyEvent.VK_UP, oldUp);
- putBackAction(im, am, KeyEvent.VK_DOWN, oldDown);
- putBackAction(im, am, KeyEvent.VK_LEFT, oldLeft);
- putBackAction(im, am, KeyEvent.VK_RIGHT, oldRight);
- putBackAction(im, am, KeyEvent.VK_ENTER, oldEnter);
- putBackAction(im, am, KeyEvent.VK_TAB, oldTab);
- putBackAction(im, am, KeyEvent.VK_HOME, oldHome);
- putBackAction(im, am, KeyEvent.VK_END, oldEnd);
- putBackAction(im, am, KeyEvent.VK_PAGE_UP, oldPageUp);
- putBackAction(im, am, KeyEvent.VK_PAGE_DOWN, oldPageDown);
-
- // Ctrl+C
- KeyStroke ks = getCopyKeyStroke();
- am.put(im.get(ks), oldCtrlC.action); // Original action
- im.put(ks, oldCtrlC.key); // Original key
-
- comp.removeCaretListener(this);
-
- }
-
-
- /**
- * Updates the LookAndFeel of this window and the description
- * window.
- */
- public void updateUI() {
- SwingUtilities.updateComponentTreeUI(this);
- if (descWindow != null) {
- descWindow.updateUI();
- }
- }
-
-
- /**
- * Called when a new item is selected in the popup list.
- *
- * @param e The event.
- */
- public void valueChanged(ListSelectionEvent e) {
- if (!e.getValueIsAdjusting()) {
- Object value = list.getSelectedValue();
- if (value != null && descWindow != null) {
- descWindow.setDescriptionFor((Completion) value);
- positionDescWindow();
- }
- }
- }
-
-
- class CopyAction extends AbstractAction {
-
- public void actionPerformed(ActionEvent e) {
- boolean doNormalCopy = false;
- if (descWindow != null && descWindow.isVisible()) {
- doNormalCopy = !descWindow.copy();
- }
- if (doNormalCopy) {
- ac.getTextComponent().copy();
- }
- }
-
- }
-
-
- class DownAction extends AbstractAction {
-
- public void actionPerformed(ActionEvent e) {
- if (isVisible()) {
- selectNextItem();
- refreshInstallComp();
- }
- }
-
- }
-
-
- class EndAction extends AbstractAction {
-
- public void actionPerformed(ActionEvent e) {
- if (isVisible()) {
- selectLastItem();
- }
- }
-
- }
-
-
- class EnterAction extends AbstractAction {
-
- public void actionPerformed(ActionEvent e) {
- if (isVisible()) {
- insertSelectedCompletion();
- refreshInstallComp();
- }
- }
-
+ super(parent,ac);
}
-
- class EscapeAction extends AbstractAction {
-
- public void actionPerformed(ActionEvent e) {
- if (isVisible()) {
- setVisible(false);
- }
- }
-
- }
-
-
- class HomeAction extends AbstractAction {
-
- public void actionPerformed(ActionEvent e) {
- if (isVisible()) {
- selectFirstItem();
- }
- }
-
- }
-
-
- /**
- * A mapping from a key (an Object) to an Action.
- */
- private static class KeyActionPair {
-
- public Object key;
- public Action action;
-
- public KeyActionPair() {
- }
-
- public KeyActionPair(Object key, Action a) {
- this.key = key;
- this.action = a;
- }
-
- }
-
-
- class LeftAction extends AbstractAction {
-
- public void actionPerformed(ActionEvent e) {
- if (isVisible()) {
- JTextComponent comp = ac.getTextComponent();
- Caret c = comp.getCaret();
- int dot = c.getDot();
- if (dot > 0) {
- c.setDot(--dot);
- // Ensure moving left hasn't moved us up a line, thus
- // hiding the popup window.
- if (comp.isVisible()) {
- if (lastLine != -1) {
- doAutocomplete();
- }
- }
- }
- }
- }
-
- }
-
-
- class PageDownAction extends AbstractAction {
-
- public void actionPerformed(ActionEvent e) {
- if (isVisible()) {
- selectPageDownItem();
- }
- }
-
- }
-
-
- class PageUpAction extends AbstractAction {
-
- public void actionPerformed(ActionEvent e) {
- if (isVisible()) {
- selectPageUpItem();
- }
- }
-
- }
-
-
- /**
- * The actual list of completion choices in this popup window.
- */
- private class PopupList extends JList {
-
- public PopupList(CompletionListModel model) {
- super(model);
- }
-
- @Override
- public void setUI(ListUI ui) {
- if (Util.getUseSubstanceRenderers() &&
- SUBSTANCE_LIST_UI.equals(ui.getClass().getName())) {
- // Substance requires its special ListUI be installed for
- // its renderers to actually render (!), but long completion
- // lists (e.g. PHPCompletionProvider in RSTALanguageSupport)
- // will simply populate too slowly on initial display (when
- // calculating preferred size of all items), so in this case
- // we give a prototype cell value.
- CompletionProvider p = ac.getCompletionProvider();
- BasicCompletion bc = new BasicCompletion(p, "Hello world");
- setPrototypeCellValue(bc);
- } else {
- // Our custom UI that is faster for long HTML completion lists.
- ui = new FastListUI();
- setPrototypeCellValue(null);
- }
- super.setUI(ui);
- }
-
- }
-
-
- class RightAction extends AbstractAction {
-
- public void actionPerformed(ActionEvent e) {
- if (isVisible()) {
- JTextComponent comp = ac.getTextComponent();
- Caret c = comp.getCaret();
- int dot = c.getDot();
- if (dot < comp.getDocument().getLength()) {
- c.setDot(++dot);
- // Ensure moving right hasn't moved us up a line, thus
- // hiding the popup window.
- if (comp.isVisible()) {
- if (lastLine != -1) {
- doAutocomplete();
- }
- }
- }
- }
- }
-
- }
-
-
- class UpAction extends AbstractAction {
-
- public void actionPerformed(ActionEvent e) {
- if (isVisible()) {
- selectPreviousItem();
- refreshInstallComp();
- }
- }
-
- }
-
-
}
\ No newline at end of file
diff --git a/designer-base/src/main/java/com/fr/design/gui/autocomplete/FormulaPaneAutoCompletion.java b/designer-base/src/main/java/com/fr/design/gui/autocomplete/FormulaPaneAutoCompletion.java
index 8168c767d5..cd37fd9bc4 100644
--- a/designer-base/src/main/java/com/fr/design/gui/autocomplete/FormulaPaneAutoCompletion.java
+++ b/designer-base/src/main/java/com/fr/design/gui/autocomplete/FormulaPaneAutoCompletion.java
@@ -2,226 +2,18 @@ package com.fr.design.gui.autocomplete;
import com.fr.design.formula.FormulaPane;
-import javax.swing.AbstractAction;
-import javax.swing.Action;
-import javax.swing.ActionMap;
-import javax.swing.InputMap;
-import javax.swing.JComponent;
import javax.swing.KeyStroke;
import javax.swing.ListCellRenderer;
-import javax.swing.SwingUtilities;
-import javax.swing.Timer;
-import javax.swing.UIManager;
-import javax.swing.event.CaretEvent;
-import javax.swing.event.CaretListener;
-import javax.swing.event.DocumentEvent;
-import javax.swing.event.DocumentListener;
-import javax.swing.text.BadLocationException;
import javax.swing.text.Caret;
-import javax.swing.text.Document;
-import javax.swing.text.Element;
import javax.swing.text.JTextComponent;
-import java.awt.ComponentOrientation;
-import java.awt.Dimension;
-import java.awt.Point;
-import java.awt.Rectangle;
import java.awt.Window;
-import java.awt.event.ActionEvent;
-import java.awt.event.ActionListener;
-import java.awt.event.ComponentAdapter;
-import java.awt.event.ComponentEvent;
-import java.awt.event.FocusAdapter;
-import java.awt.event.FocusEvent;
-import java.awt.event.HierarchyEvent;
-import java.awt.event.HierarchyListener;
-import java.awt.event.KeyEvent;
-import java.awt.event.WindowEvent;
-import java.awt.event.WindowFocusListener;
-import java.beans.PropertyChangeEvent;
-import java.beans.PropertyChangeListener;
-import java.util.List;
-
-import static com.fr.design.gui.syntax.ui.rtextarea.RTADefaultInputMap.DEFAULT_MODIFIER;
/**
* @author Hoky
* @date 2021/11/2
* @description 重写一个支持刷新公式树组件的AutoCompletion
*/
-public class FormulaPaneAutoCompletion extends AutoCompletion {
- private FormulaPane.VariableTreeAndDescriptionArea area;
-
- /**
- * The text component we're providing completion for.
- */
- private JTextComponent textComponent;
-
- /**
- * The parent window of {@link #textComponent}.
- */
- private Window parentWindow;
-
- /**
- * The popup window containing completion choices.
- */
- private FormulaAutoCompletePopupWindow popupWindow;
-
- /**
- * The preferred size of the completion choices window. This field exists
- * because the user will likely set the preferred size of the window
- * before it is actually created.
- */
- private Dimension preferredChoicesWindowSize;
-
- /**
- * The preferred size of the optional description window. This field
- * only exists because the user may (and usually will) set the size of
- * the description window before it exists (it must be parented to a
- * Window).
- */
- private Dimension preferredDescWindowSize;
-
- /**
- * Manages any parameterized completions that are inserted.
- */
- private ParameterizedCompletionContext pcc;
-
- /**
- * Provides the completion options relevant to the current caret position.
- */
- private CompletionProvider provider;
-
- /**
- * The renderer to use for the completion choices. If this is
- * null
, then a default renderer is used.
- */
- private ListCellRenderer renderer;
-
- /**
- * The handler to use when an external URL is clicked in the help
- * documentation.
- */
- private ExternalURLHandler externalURLHandler;
-
- /**
- * An optional redirector that converts URL's to some other location before
- * being handed over to externalURLHandler
.
- */
- private static LinkRedirector linkRedirector;
-
- /**
- * Whether the description window should be displayed along with the
- * completion choice window.
- */
- private boolean showDescWindow;
-
- /**
- * Whether auto-complete is enabled.
- */
- private boolean autoCompleteEnabled;
-
- /**
- * Whether the auto-activation of auto-complete (after a delay, after the
- * user types an appropriate character) is enabled.
- */
- private boolean autoActivationEnabled;
-
- /**
- * Whether or not, when there is only a single auto-complete option
- * that matches the text at the current text position, that text should
- * be auto-inserted, instead of the completion window displaying.
- */
- private boolean autoCompleteSingleChoices;
-
- /**
- * Whether parameter assistance is enabled.
- */
- private boolean parameterAssistanceEnabled;
-
- /**
- * A renderer used for {@link Completion}s in the optional parameter
- * choices popup window (displayed when a {@link ParameterizedCompletion}
- * is code-completed). If this isn't set, a default renderer is used.
- */
- private ListCellRenderer paramChoicesRenderer;
-
- /**
- * The keystroke that triggers the completion window.
- */
- private KeyStroke trigger;
-
- /**
- * The previous key in the text component's InputMap
for the
- * trigger key.
- */
- private Object oldTriggerKey;
-
- /**
- * The action previously assigned to {@link #trigger}, so we can reset it
- * if the user disables auto-completion.
- */
- private Action oldTriggerAction;
-
- /**
- * The previous key in the text component's InputMap
for the
- * parameter completion trigger key.
- */
- private Object oldParenKey;
-
- /**
- * The action previously assigned to the parameter completion key, so we
- * can reset it when we uninstall.
- */
- private Action oldParenAction;
-
- /**
- * Listens for events in the parent window that affect the visibility of
- * the popup windows.
- */
- private ParentWindowListener parentWindowListener;
-
- /**
- * Listens for events from the text component that affect the visibility
- * of the popup windows.
- */
- private TextComponentListener textComponentListener;
-
- /**
- * Listens for events in the text component that cause the popup windows
- * to automatically activate.
- */
- private AutoActivationListener autoActivationListener;
-
- /**
- * Listens for LAF changes so the auto-complete windows automatically
- * update themselves accordingly.
- */
- private LookAndFeelChangeListener lafListener;
-
- /**
- * The key used in the input map for the AutoComplete action.
- */
- private static final String PARAM_TRIGGER_KEY = "AutoComplete";
-
- /**
- * Key used in the input map for the parameter completion action.
- */
- private static final String PARAM_COMPLETE_KEY = "AutoCompletion.FunctionStart";
-
- /**
- * Stores how to render auto-completion-specific highlights in text
- * components.
- */
- private static final AutoCompletionStyleContext STYLE_CONTEXT =
- new AutoCompletionStyleContext();
-
- /**
- * Whether debug messages should be printed to stdout as AutoCompletion
- * runs.
- */
- private static final boolean DEBUG = initDebug();
-
-
+public class FormulaPaneAutoCompletion extends AutoCompletionWithExtraRefresh {
/**
* Constructor.
*
@@ -230,957 +22,29 @@ public class FormulaPaneAutoCompletion extends AutoCompletion {
*/
public FormulaPaneAutoCompletion(CompletionProvider provider) {
super(provider);
- setChoicesWindowSize(350, 200);
- setDescriptionWindowSize(350, 250);
-
- setCompletionProvider(provider);
- setTriggerKey(getDefaultTriggerKey());
- setAutoCompleteEnabled(true);
- setAutoCompleteSingleChoices(true);
- setAutoActivationEnabled(false);
- setShowDescWindow(false);
- parentWindowListener = new ParentWindowListener();
- textComponentListener = new TextComponentListener();
- autoActivationListener = new AutoActivationListener();
- lafListener = new LookAndFeelChangeListener();
- }
-
-
- /**
- * Displays the popup window. Hosting applications can call this method
- * to programmatically begin an auto-completion operation.
- */
- public void doCompletion() {
- refreshPopupWindow();
- }
-
-
- /**
- * Returns the delay between when the user types a character and when the
- * code completion popup should automatically appear (if applicable).
- *
- * @return The delay, in milliseconds.
- * @see #setAutoActivationDelay(int)
- */
- public int getAutoActivationDelay() {
- return autoActivationListener.timer.getDelay();
}
-
- /**
- * Returns whether, if a single auto-complete choice is available, it
- * should be automatically inserted, without displaying the popup menu.
- *
- * @return Whether to auto-complete single choices.
- * @see #setAutoCompleteSingleChoices(boolean)
- */
- public boolean isAutoCompleteSingleChoices() {
- return autoCompleteSingleChoices;
- }
-
-
- /**
- * Returns the completion provider.
- *
- * @return The completion provider.
- */
- public CompletionProvider getCompletionProvider() {
- return provider;
- }
-
-
- /**
- * Returns whether debug is enabled for AutoCompletion.
- *
- * @return Whether debug is enabled.
- */
- static boolean isDebug() {
- return DEBUG;
- }
-
-
- /**
- * Returns the default auto-complete "trigger key" for this OS. For
- * Windows, for example, it is Ctrl+Space.
- *
- * @return The default auto-complete trigger key.
- */
- public static KeyStroke getDefaultTriggerKey() {
- // Default to CTRL, even on Mac, since Ctrl+Space activates Spotlight
- return KeyStroke.getKeyStroke(KeyEvent.VK_SPACE, DEFAULT_MODIFIER);
- }
-
-
- /**
- * Returns the handler to use when an external URL is clicked in the
- * description window.
- *
- * @return The handler.
- * @see #setExternalURLHandler(ExternalURLHandler)
- * @see #getLinkRedirector()
- */
- public ExternalURLHandler getExternalURLHandler() {
- return externalURLHandler;
- }
-
-
- int getLineOfCaret() {
- Document doc = textComponent.getDocument();
- Element root = doc.getDefaultRootElement();
- return root.getElementIndex(textComponent.getCaretPosition());
- }
-
-
- /**
- * Returns the link redirector, if any.
- *
- * @return The link redirector, or null
if none.
- * @see #setLinkRedirector(LinkRedirector)
- */
- public static LinkRedirector getLinkRedirector() {
- return linkRedirector;
- }
-
-
- /**
- * Returns the default list cell renderer used when a completion provider
- * does not supply its own.
- *
- * @return The default list cell renderer.
- * @see #setListCellRenderer(ListCellRenderer)
- */
- public ListCellRenderer getListCellRenderer() {
- return renderer;
- }
-
-
- /**
- * Returns the renderer to use for {@link Completion}s in the optional
- * parameter choices popup window (displayed when a
- * {@link ParameterizedCompletion} is code-completed). If this returns
- * null
, a default renderer is used.
- *
- * @return The renderer to use.
- * @see #setParamChoicesRenderer(ListCellRenderer)
- * @see #isParameterAssistanceEnabled()
- */
- public ListCellRenderer getParamChoicesRenderer() {
- return paramChoicesRenderer;
- }
-
-
- /**
- * Returns the text to replace with in the document. This is a
- * "last-chance" hook for subclasses to make special modifications to the
- * completion text inserted. The default implementation simply returns
- * c.getReplacementText(). You usually will not need to modify
- * this method.
- *
- * @param c The completion being inserted.
- * @param doc The document being modified.
- * @param start The start of the text being replaced.
- * @param len The length of the text being replaced.
- * @return The text to replace with.
- */
- protected String getReplacementText(Completion c, Document doc, int start,
- int len) {
- return c.getReplacementText();
- }
-
-
- /**
- * Returns whether the "description window" should be shown alongside
- * the completion window.
- *
- * @return Whether the description window should be shown.
- * @see #setShowDescWindow(boolean)
- */
- public boolean isShowDescWindow() {
- return showDescWindow;
- }
-
-
- /**
- * Returns the style context describing how auto-completion related
- * highlights in the editor are rendered.
- *
- * @return The style context.
- */
- public static AutoCompletionStyleContext getStyleContext() {
- return STYLE_CONTEXT;
- }
-
-
- /**
- * Returns the text component for which auto-completion is enabled.
- *
- * @return The text component, or null
if this
- * {@link AutoCompletion} is not installed on any text component.
- * @see #install(JTextComponent)
- */
- public JTextComponent getTextComponent() {
- return textComponent;
- }
-
-
- /**
- * Returns the orientation of the text component we're installed to.
- *
- * @return The orientation of the text component, or null
if
- * we are not installed on one.
- */
- ComponentOrientation getTextComponentOrientation() {
- return textComponent == null ? null :
- textComponent.getComponentOrientation();
- }
-
-
- /**
- * Returns the "trigger key" used for auto-complete.
- *
- * @return The trigger key.
- * @see #setTriggerKey(KeyStroke)
- */
- public KeyStroke getTriggerKey() {
- return trigger;
- }
-
-
- /**
- * Hides any child windows being displayed by the auto-completion system.
- *
- * @return Whether any windows were visible.
- */
- public boolean hideChildWindows() {
- //return hidePopupWindow() || hideToolTipWindow();
- boolean res = hidePopupWindow();
- res |= hideParameterCompletionPopups();
- return res;
- }
-
-
- /**
- * Hides and disposes of any parameter completion-related popups.
- *
- * @return Whether any such windows were visible (and thus hidden).
- */
- private boolean hideParameterCompletionPopups() {
- if (pcc != null) {
- pcc.deactivate();
- pcc = null;
- return true;
- }
- return false;
- }
-
-
- /**
- * Hides the popup window, if it is visible.
- *
- * @return Whether the popup window was visible.
- */
- private boolean hidePopupWindow() {
- if (popupWindow != null) {
- if (popupWindow.isVisible()) {
- popupWindow.setVisible(false);
- return true;
- }
- }
- return false;
- }
-
-
- /**
- * Determines whether debug should be enabled for the AutoCompletion
- * library. This method checks a system property, but takes care of
- * {@link SecurityException}s in case we're in an applet or WebStart.
- *
- * @return Whether debug should be enabled.
- */
- private static boolean initDebug() {
- boolean debug = false;
- try {
- debug = Boolean.getBoolean("AutoCompletion.debug");
- } catch (SecurityException se) { // We're in an applet or WebStart.
- debug = false;
- }
- return debug;
- }
-
-
- /**
- * Installs this auto-completion on a text component. If this
- * {@link AutoCompletion} is already installed on another text component,
- * it is uninstalled first.
- *
- * @param c The text component.
- * @see #uninstall()
- */
- public void install(JTextComponent c) {
-
- if (textComponent != null) {
- uninstall();
- }
-
- this.textComponent = c;
- installTriggerKey(getTriggerKey());
-
- // Install the function completion key, if there is one.
- // NOTE: We cannot do this if the start char is ' ' (e.g. just a space
- // between the function name and parameters) because it overrides
- // RSTA's special space action. It seems KeyStorke.getKeyStroke(' ')
- // hoses ctrl+space, shift+space, etc., even though I think it
- // shouldn't...
- char start = provider.getParameterListStart();
- if (start != 0 && start != ' ') {
- InputMap im = c.getInputMap();
- ActionMap am = c.getActionMap();
- KeyStroke ks = KeyStroke.getKeyStroke(start);
- oldParenKey = im.get(ks);
- im.put(ks, PARAM_COMPLETE_KEY);
- oldParenAction = am.get(PARAM_COMPLETE_KEY);
- am.put(PARAM_COMPLETE_KEY,
- new ParameterizedCompletionStartAction(start));
- }
-
- textComponentListener.addTo(this.textComponent);
- // In case textComponent is already in a window...
- textComponentListener.hierarchyChanged(null);
-
- if (isAutoActivationEnabled()) {
- autoActivationListener.addTo(this.textComponent);
- }
-
- UIManager.addPropertyChangeListener(lafListener);
- updateUI(); // In case there have been changes since we uninstalled
-
- }
-
-
- /**
- * Installs a "trigger key" action onto the current text component.
- *
- * @param ks The keystroke that should trigger the action.
- */
- private void installTriggerKey(KeyStroke ks) {
- InputMap im = textComponent.getInputMap();
- oldTriggerKey = im.get(ks);
- im.put(ks, PARAM_TRIGGER_KEY);
- ActionMap am = textComponent.getActionMap();
- oldTriggerAction = am.get(PARAM_TRIGGER_KEY);
- am.put(PARAM_TRIGGER_KEY, new AutoCompleteAction());
- }
-
-
- /**
- * Returns whether auto-activation is enabled (that is, whether the
- * completion popup will automatically appear after a delay when the user
- * types an appropriate character). Note that this parameter will be
- * ignored if auto-completion is disabled.
- *
- * @return Whether auto-activation is enabled.
- * @see #setAutoActivationEnabled(boolean)
- * @see #getAutoActivationDelay()
- * @see #isAutoCompleteEnabled()
- */
- public boolean isAutoActivationEnabled() {
- return autoActivationEnabled;
- }
-
-
- /**
- * Returns whether auto-completion is enabled.
- *
- * @return Whether auto-completion is enabled.
- * @see #setAutoCompleteEnabled(boolean)
- */
- public boolean isAutoCompleteEnabled() {
- return autoCompleteEnabled;
- }
-
-
- /**
- * Returns whether parameter assistance is enabled.
- *
- * @return Whether parameter assistance is enabled.
- * @see #setParameterAssistanceEnabled(boolean)
- */
- public boolean isParameterAssistanceEnabled() {
- return parameterAssistanceEnabled;
- }
-
-
- /**
- * Returns whether the completion popup window is visible.
- *
- * @return Whether the completion popup window is visible.
- */
- public boolean isPopupVisible() {
- return popupWindow != null && popupWindow.isVisible();
- }
-
- private boolean needSetPopupWindow(int count, int textLen) {
- return (count == 1 && (isPopupVisible() || textLen == 0))
- || (count == 1 && !isAutoCompleteSingleChoices())
- || count > 1;
- }
-
- private FormulaAutoCompletePopupWindow createAutoCompletePopupWindow() {
- FormulaAutoCompletePopupWindow popupWindow = new FormulaAutoCompletePopupWindow(parentWindow, this);
+ @Override
+ protected AutoCompleteWithExtraRefreshPopupWindow createAutoCompletePopupWindow() {
+ FormulaAutoCompletePopupWindow popupWindow = new FormulaAutoCompletePopupWindow(getParentWindow(), this);
// Completion is usually done for code, which is always done
// LTR, so make completion stuff RTL only if text component is
// also RTL.
popupWindow.applyComponentOrientation(
getTextComponentOrientation());
- if (renderer != null) {
- popupWindow.setListCellRenderer(renderer);
+ if (getCellRender() != null) {
+ popupWindow.setListCellRenderer(getCellRender());
}
- if (preferredChoicesWindowSize != null) {
- popupWindow.setSize(preferredChoicesWindowSize);
+ if (getPreferredChoicesWindowSize() != null) {
+ popupWindow.setSize(getPreferredChoicesWindowSize());
}
- if (preferredDescWindowSize != null) {
+ if (getPreferredDescWindowSize() != null) {
popupWindow.setDescriptionWindowSize(
- preferredDescWindowSize);
+ getPreferredDescWindowSize());
}
return popupWindow;
}
- /**
- * Sets the delay between when the user types a character and when the
- * code completion popup should automatically appear (if applicable).
- *
- * @param ms The delay, in milliseconds. This should be greater than zero.
- * @see #getAutoActivationDelay()
- */
- public void setAutoActivationDelay(int ms) {
- ms = Math.max(0, ms);
- autoActivationListener.timer.stop();
- autoActivationListener.timer.setInitialDelay(ms);
- }
-
-
- /**
- * Toggles whether auto-activation is enabled. Note that auto-activation
- * also depends on auto-completion itself being enabled.
- *
- * @param enabled Whether auto-activation is enabled.
- * @see #isAutoActivationEnabled()
- * @see #setAutoActivationDelay(int)
- */
- public void setAutoActivationEnabled(boolean enabled) {
- if (enabled != autoActivationEnabled) {
- autoActivationEnabled = enabled;
- if (textComponent != null) {
- if (autoActivationEnabled) {
- autoActivationListener.addTo(textComponent);
- } else {
- autoActivationListener.removeFrom(textComponent);
- }
- }
- }
- }
-
-
- /**
- * Sets whether auto-completion is enabled.
- *
- * @param enabled Whether auto-completion is enabled.
- * @see #isAutoCompleteEnabled()
- */
- public void setAutoCompleteEnabled(boolean enabled) {
- if (enabled != autoCompleteEnabled) {
- autoCompleteEnabled = enabled;
- hidePopupWindow();
- }
- }
-
-
- /**
- * Sets whether, if a single auto-complete choice is available, it should
- * be automatically inserted, without displaying the popup menu.
- *
- * @param autoComplete Whether to auto-complete single choices.
- * @see #isAutoCompleteSingleChoices()
- */
- public void setAutoCompleteSingleChoices(boolean autoComplete) {
- autoCompleteSingleChoices = autoComplete;
- }
-
-
- /**
- * Sets the completion provider being used.
- *
- * @param provider The new completion provider. This cannot be
- * null
.
- * @throws IllegalArgumentException If provider
is
- * null
.
- */
- public void setCompletionProvider(CompletionProvider provider) {
- if (provider == null) {
- throw new IllegalArgumentException("provider cannot be null");
- }
- this.provider = provider;
- hidePopupWindow(); // In case new choices should be displayed.
- }
-
-
- /**
- * Sets the size of the completion choices window.
- *
- * @param w The new width.
- * @param h The new height.
- * @see #setDescriptionWindowSize(int, int)
- */
- public void setChoicesWindowSize(int w, int h) {
- preferredChoicesWindowSize = new Dimension(w, h);
- if (popupWindow != null) {
- popupWindow.setSize(preferredChoicesWindowSize);
- }
- }
-
-
- /**
- * Sets the size of the description window.
- *
- * @param w The new width.
- * @param h The new height.
- * @see #setChoicesWindowSize(int, int)
- */
- public void setDescriptionWindowSize(int w, int h) {
- preferredDescWindowSize = new Dimension(w, h);
- if (popupWindow != null) {
- popupWindow.setDescriptionWindowSize(preferredDescWindowSize);
- }
- }
-
-
- /**
- * Sets the handler to use when an external URL is clicked in the
- * description window. This handler can perform some action, such as
- * open the URL in a web browser. The default implementation will open
- * the URL in a browser, but only if running in Java 6. If you want
- * browser support for Java 5 and below, or otherwise want to respond to
- * hyperlink clicks, you will have to install your own handler to do so.
- *
- * @param handler The new handler.
- * @see #getExternalURLHandler()
- */
- public void setExternalURLHandler(ExternalURLHandler handler) {
- this.externalURLHandler = handler;
- }
-
-
- /**
- * Sets the redirector for external URL's found in code completion
- * documentation. When a non-local link in completion popups is clicked,
- * this redirector is given the chance to modify the URL fetched and
- * displayed.
- *
- * @param redirector The link redirector, or null
for
- * none.
- * @see #getLinkRedirector()
- */
- public static void setLinkRedirector(LinkRedirector redirector) {
- linkRedirector = redirector;
- }
-
-
- /**
- * Sets the default list cell renderer to use when a completion provider
- * does not supply its own.
- *
- * @param renderer The renderer to use. If this is null
,
- * a default renderer is used.
- * @see #getListCellRenderer()
- */
- public void setListCellRenderer(ListCellRenderer renderer) {
- this.renderer = renderer;
- if (popupWindow != null) {
- popupWindow.setListCellRenderer(renderer);
- hidePopupWindow();
- }
- }
-
-
- /**
- * Sets the renderer to use for {@link Completion}s in the optional
- * parameter choices popup window (displayed when a
- * {@link ParameterizedCompletion} is code-completed). If this isn't set,
- * a default renderer is used.
- *
- * @param r The renderer to use.
- * @see #getParamChoicesRenderer()
- * @see #setParameterAssistanceEnabled(boolean)
- */
- public void setParamChoicesRenderer(ListCellRenderer r) {
- paramChoicesRenderer = r;
- }
-
-
- /**
- * Sets whether parameter assistance is enabled. If parameter assistance
- * is enabled, and a "parameterized" completion (such as a function or
- * method) is inserted, the user will get "assistance" in inserting the
- * parameters in the form of a popup window with documentation and easy
- * tabbing through the arguments (as seen in Eclipse and NetBeans).
- *
- * @param enabled Whether parameter assistance should be enabled.
- * @see #isParameterAssistanceEnabled()
- */
- public void setParameterAssistanceEnabled(boolean enabled) {
- parameterAssistanceEnabled = enabled;
- }
-
-
- /**
- * Sets whether the "description window" should be shown beside the
- * completion window.
- *
- * @param show Whether to show the description window.
- * @see #isShowDescWindow()
- */
- public void setShowDescWindow(boolean show) {
- hidePopupWindow(); // Needed to force it to take effect
- showDescWindow = show;
- }
-
-
- /**
- * Sets the keystroke that should be used to trigger the auto-complete
- * popup window.
- *
- * @param ks The keystroke.
- * @throws IllegalArgumentException If ks
is null
.
- * @see #getTriggerKey()
- */
- public void setTriggerKey(KeyStroke ks) {
- if (ks == null) {
- throw new IllegalArgumentException("trigger key cannot be null");
- }
- if (!ks.equals(trigger)) {
- if (textComponent != null) {
- // Put old trigger action back.
- uninstallTriggerKey();
- // Grab current action for new trigger and replace it.
- installTriggerKey(ks);
- }
- trigger = ks;
- }
- }
-
-
- /**
- * Uninstalls this auto-completion from its text component. If it is not
- * installed on any text component, nothing happens.
- *
- * @see #install(JTextComponent)
- */
- public void uninstall() {
-
- if (textComponent != null) {
-
- hidePopupWindow(); // Unregisters listeners, actions, etc.
-
- uninstallTriggerKey();
-
- // Uninstall the function completion key.
- char start = provider.getParameterListStart();
- if (start != 0) {
- KeyStroke ks = KeyStroke.getKeyStroke(start);
- InputMap im = textComponent.getInputMap();
- im.put(ks, oldParenKey);
- ActionMap am = textComponent.getActionMap();
- am.put(PARAM_COMPLETE_KEY, oldParenAction);
- }
-
- textComponentListener.removeFrom(textComponent);
- if (parentWindow != null) {
- parentWindowListener.removeFrom(parentWindow);
- }
-
- if (isAutoActivationEnabled()) {
- autoActivationListener.removeFrom(textComponent);
- }
-
- UIManager.removePropertyChangeListener(lafListener);
-
- textComponent = null;
- popupWindow = null;
-
- }
-
- }
-
-
- /**
- * Replaces the "trigger key" action with the one that was there
- * before auto-completion was installed.
- */
- private void uninstallTriggerKey() {
- InputMap im = textComponent.getInputMap();
- im.put(trigger, oldTriggerKey);
- ActionMap am = textComponent.getActionMap();
- am.put(PARAM_TRIGGER_KEY, oldTriggerAction);
- }
-
-
- /**
- * Updates the LookAndFeel of the popup window. Applications can call
- * this method as appropriate if they support changing the LookAndFeel
- * at runtime.
- */
- private void updateUI() {
- if (popupWindow != null) {
- popupWindow.updateUI();
- }
- if (pcc != null) {
- pcc.updateUI();
- }
- // Will practically always be a JComponent (a JLabel)
- if (paramChoicesRenderer instanceof JComponent) {
- ((JComponent) paramChoicesRenderer).updateUI();
- }
- }
-
-
- /**
- * Listens for events in the text component to auto-activate the code
- * completion popup.
- */
- private class AutoActivationListener extends FocusAdapter
- implements DocumentListener, CaretListener, ActionListener {
-
- private Timer timer;
- private boolean justInserted;
-
- public AutoActivationListener() {
- timer = new Timer(200, this);
- timer.setRepeats(false);
- }
-
- public void actionPerformed(ActionEvent e) {
- doCompletion();
- }
-
- public void addTo(JTextComponent tc) {
- tc.addFocusListener(this);
- tc.getDocument().addDocumentListener(this);
- tc.addCaretListener(this);
- }
-
- public void caretUpdate(CaretEvent e) {
- if (justInserted) {
- justInserted = false;
- } else {
- timer.stop();
- }
- }
-
- public void changedUpdate(DocumentEvent e) {
- // Ignore
- }
-
- @Override
- public void focusLost(FocusEvent e) {
- timer.stop();
- //hideChildWindows(); Other listener will do this
- }
-
- public void insertUpdate(DocumentEvent e) {
- justInserted = false;
- if (isAutoCompleteEnabled() && isAutoActivationEnabled() &&
- e.getLength() == 1) {
- if (provider.isAutoActivateOkay(textComponent)) {
- timer.restart();
- justInserted = true;
- } else {
- timer.stop();
- }
- } else {
- timer.stop();
- }
- }
-
- public void removeFrom(JTextComponent tc) {
- tc.removeFocusListener(this);
- tc.getDocument().removeDocumentListener(this);
- tc.removeCaretListener(this);
- timer.stop();
- justInserted = false;
- }
-
- public void removeUpdate(DocumentEvent e) {
- timer.stop();
- }
-
- }
-
-
- /**
- * The Action
that displays the popup window if
- * auto-completion is enabled.
- */
- private class AutoCompleteAction extends AbstractAction {
-
- public void actionPerformed(ActionEvent e) {
- if (isAutoCompleteEnabled()) {
- refreshPopupWindow();
- } else if (oldTriggerAction != null) {
- oldTriggerAction.actionPerformed(e);
- }
- }
-
- }
-
-
- /**
- * Listens for LookAndFeel changes and updates the various popup windows
- * involved in auto-completion accordingly.
- */
- private class LookAndFeelChangeListener implements PropertyChangeListener {
-
- public void propertyChange(PropertyChangeEvent e) {
- String name = e.getPropertyName();
- if ("lookAndFeel".equals(name)) {
- updateUI();
- }
- }
-
- }
-
-
- /**
- * Action that starts a parameterized completion, e.g. after '(' is
- * typed.
- */
- private class ParameterizedCompletionStartAction extends AbstractAction {
-
- private String start;
-
- public ParameterizedCompletionStartAction(char ch) {
- this.start = Character.toString(ch);
- }
-
- public void actionPerformed(ActionEvent e) {
-
- // Prevents keystrokes from messing up
- boolean wasVisible = hidePopupWindow();
-
- // Only proceed if they were selecting a completion
- if (!wasVisible || !isParameterAssistanceEnabled()) {
- textComponent.replaceSelection(start);
- return;
- }
-
- Completion c = popupWindow.getSelection();
- if (c instanceof ParameterizedCompletion) { // Should always be true
- // Fixes capitalization of the entered text.
- insertCompletion(c, true);
- }
-
- }
-
- }
-
-
- /**
- * Listens for events in the parent window of the text component with
- * auto-completion enabled.
- */
- private class ParentWindowListener extends ComponentAdapter
- implements WindowFocusListener {
-
- public void addTo(Window w) {
- w.addComponentListener(this);
- w.addWindowFocusListener(this);
- }
-
- @Override
- public void componentHidden(ComponentEvent e) {
- hideChildWindows();
- }
-
- @Override
- public void componentMoved(ComponentEvent e) {
- hideChildWindows();
- }
-
- @Override
- public void componentResized(ComponentEvent e) {
- hideChildWindows();
- }
-
- public void removeFrom(Window w) {
- w.removeComponentListener(this);
- w.removeWindowFocusListener(this);
- }
-
- public void windowGainedFocus(WindowEvent e) {
- }
-
- public void windowLostFocus(WindowEvent e) {
- hideChildWindows();
- }
-
- }
-
-
- /**
- * Listens for events from the text component we're installed on.
- */
- private class TextComponentListener extends FocusAdapter
- implements HierarchyListener {
-
- void addTo(JTextComponent tc) {
- tc.addFocusListener(this);
- tc.addHierarchyListener(this);
- }
-
- /**
- * Hide the auto-completion windows when the text component loses
- * focus.
- */
- @Override
- public void focusLost(FocusEvent e) {
- hideChildWindows();
- }
-
- /**
- * Called when the component hierarchy for our text component changes.
- * When the text component is added to a new {@link Window}, this
- * method registers listeners on that Window
.
- *
- * @param e The event.
- */
- public void hierarchyChanged(HierarchyEvent e) {
-
- // NOTE: e many be null as we call this method at other times.
- //System.out.println("Hierarchy changed! " + e);
-
- Window oldParentWindow = parentWindow;
- parentWindow = SwingUtilities.getWindowAncestor(textComponent);
- if (parentWindow != oldParentWindow) {
- if (oldParentWindow != null) {
- parentWindowListener.removeFrom(oldParentWindow);
- }
- if (parentWindow != null) {
- parentWindowListener.addTo(parentWindow);
- }
- }
-
- }
-
- public void removeFrom(JTextComponent tc) {
- tc.removeFocusListener(this);
- tc.removeHierarchyListener(this);
- }
-
- }
-
- public void installVariableTree(FormulaPane.VariableTreeAndDescriptionArea jComp) {
- area = jComp;
- }
-
/**
* Inserts a completion. Any time a code completion event occurs, the
* actual text insertion happens through this method.
@@ -1223,89 +87,4 @@ public class FormulaPaneAutoCompletion extends AutoCompletion {
}
}
-
- /**
- * Displays a "tool tip" detailing the inputs to the function just entered.
- *
- * @param pc The completion.
- * @param typedParamListStartChar Whether the parameterized completion list
- * starting character was typed.
- */
- private void startParameterizedCompletionAssistance(
- ParameterizedCompletion pc, boolean typedParamListStartChar) {
-
- // Get rid of the previous tool tip window, if there is one.
- hideParameterCompletionPopups();
-
- // Don't bother with a tool tip if there are no parameters, but if
- // they typed e.g. the opening '(', make them overtype the ')'.
- if (pc.getParamCount() == 0 && !(pc instanceof TemplateCompletion)) {
- CompletionProvider p = pc.getProvider();
- char end = p.getParameterListEnd(); // Might be '\0'
- String text = end == '\0' ? "" : Character.toString(end);
- if (typedParamListStartChar) {
- String template = "${}" + text + "${cursor}";
- textComponent.replaceSelection(Character.toString(p.getParameterListStart()));
- TemplateCompletion tc = new TemplateCompletion(p, null, null, template);
- pc = tc;
- } else {
- text = p.getParameterListStart() + text;
- textComponent.replaceSelection(text);
- return;
- }
- }
-
- pcc = new ParameterizedCompletionContext(parentWindow, this, pc);
- pcc.activate();
-
- }
-
- protected int refreshPopupWindow() {
- // A return value of null => don't suggest completions
- String text = provider.getAlreadyEnteredText(textComponent);
- if (text == null && !isPopupVisible()) {
- return getLineOfCaret();
- }
- // If the popup is currently visible, and they type a space (or any
- // character that resets the completion list to "all completions"),
- // the popup window should be hidden instead of being reset to show
- // everything.
- int textLen = text == null ? 0 : text.length();
- if (textLen == 0 && isPopupVisible()) {
- hidePopupWindow();
- return getLineOfCaret();
- }
- final List completions = provider.getCompletions(textComponent);
- int count = completions.size();
- if (needSetPopupWindow(count, textLen)) {
- if (popupWindow == null) {
- popupWindow = createAutoCompletePopupWindow();
- }
- popupWindow.installComp(area);
- popupWindow.setCompletions(completions);
- if (!popupWindow.isVisible()) {
- Rectangle r = null;
- try {
- r = textComponent.modelToView(textComponent.getCaretPosition());
- } catch (BadLocationException ble) {
- return -1;
- }
- Point p = new Point(r.x, r.y);
- SwingUtilities.convertPointToScreen(p, textComponent);
- r.x = p.x;
- r.y = p.y;
- popupWindow.setLocationRelativeTo(r);
- popupWindow.setVisible(true);
- }
- } else if (count == 1) { // !isPopupVisible && autoCompleteSingleChoices
- SwingUtilities.invokeLater(new Runnable() {
- public void run() {
- insertCompletion(completions.get(0));
- }
- });
- } else {
- hidePopupWindow();
- }
- return getLineOfCaret();
- }
}
diff --git a/designer-base/src/main/java/com/fr/design/gui/autocomplete/JSAutoCompletePopupWindow.java b/designer-base/src/main/java/com/fr/design/gui/autocomplete/JSAutoCompletePopupWindow.java
new file mode 100644
index 0000000000..80a26bf965
--- /dev/null
+++ b/designer-base/src/main/java/com/fr/design/gui/autocomplete/JSAutoCompletePopupWindow.java
@@ -0,0 +1,16 @@
+package com.fr.design.gui.autocomplete;
+
+import java.awt.Window;
+
+public class JSAutoCompletePopupWindow extends AutoCompleteWithExtraRefreshPopupWindow {
+
+ /**
+ * Constructor.
+ *
+ * @param parent The parent window (hosting the text component).
+ * @param ac The auto-completion instance.
+ */
+ public JSAutoCompletePopupWindow(Window parent, AutoCompletion ac) {
+ super(parent, ac);
+ }
+}
diff --git a/designer-base/src/main/java/com/fr/design/gui/autocomplete/JSImplPaneAutoCompletion.java b/designer-base/src/main/java/com/fr/design/gui/autocomplete/JSImplPaneAutoCompletion.java
new file mode 100644
index 0000000000..0c534bf723
--- /dev/null
+++ b/designer-base/src/main/java/com/fr/design/gui/autocomplete/JSImplPaneAutoCompletion.java
@@ -0,0 +1,31 @@
+package com.fr.design.gui.autocomplete;
+
+public class JSImplPaneAutoCompletion extends AutoCompletionWithExtraRefresh{
+ /**
+ * Constructor.
+ *
+ * @param provider The completion provider. This cannot be
+ * null
.
+ */
+ public JSImplPaneAutoCompletion(CompletionProvider provider) {
+ super(provider);
+ }
+
+ @Override
+ protected AutoCompleteWithExtraRefreshPopupWindow createAutoCompletePopupWindow() {
+ JSAutoCompletePopupWindow popupWindow = new JSAutoCompletePopupWindow(getParentWindow(),this);
+ popupWindow.applyComponentOrientation(
+ getTextComponentOrientation());
+ if (getCellRender() != null) {
+ popupWindow.setListCellRenderer(getCellRender());
+ }
+ if (getPreferredChoicesWindowSize() != null) {
+ popupWindow.setSize(getPreferredChoicesWindowSize());
+ }
+ if (getPreferredDescWindowSize() != null) {
+ popupWindow.setDescriptionWindowSize(
+ getPreferredDescWindowSize());
+ }
+ return popupWindow;
+ }
+}
diff --git a/designer-base/src/main/java/com/fr/design/gui/ilable/UIAutoChangeLineLabel.java b/designer-base/src/main/java/com/fr/design/gui/ilable/UIAutoChangeLineLabel.java
new file mode 100644
index 0000000000..d2bb0450b1
--- /dev/null
+++ b/designer-base/src/main/java/com/fr/design/gui/ilable/UIAutoChangeLineLabel.java
@@ -0,0 +1,78 @@
+package com.fr.design.gui.ilable;
+
+import javax.swing.JLabel;
+import java.awt.Dimension;
+import java.awt.FontMetrics;
+import java.util.ArrayList;
+import java.util.List;
+
+public class UIAutoChangeLineLabel extends JLabel {
+ private final String text;
+ private final int width;
+
+
+ public UIAutoChangeLineLabel(String text, int width) {
+ super(text);
+ this.text = text;
+ this.width = width;
+ }
+
+
+ @Override
+ public void doLayout() {
+ super.doLayout();
+ this.setText(wrapperHtmlText());
+ }
+
+ private String wrapperHtmlText() {
+ List stringList = autoChangeLine(this.getWidth());
+ StringBuilder builder = new StringBuilder("");
+ for (String s : stringList) {
+ //用THML标签进行拼接,以实现自动换行
+ builder.append(s).append("
");
+ }
+ builder.append("");
+ return builder.toString();
+ }
+
+ private List autoChangeLine(int width) {
+ List result = new ArrayList<>();
+ if (width <= 0) {
+ result.add(this.text);
+ } else {
+
+ char[] chars = this.text.toCharArray();
+ //获取字体计算大小
+ FontMetrics fontMetrics = this.getFontMetrics(this.getFont());
+ int start = 0;
+ int len = 0;
+ while (start + len < this.text.length()) {
+ while (true) {
+ len++;
+ if (start + len > this.text.length())
+ break;
+ if (fontMetrics.charsWidth(chars, start, len)
+ > width) {
+ break;
+ }
+ }
+ result.add(String.copyValueOf(chars, start, len - 1));
+ start = start + len - 1;
+ len = 0;
+ }
+ if (this.text.length() - start > 0) {
+ result.add(String.copyValueOf(chars, start, this.text.length() - start));
+ }
+ }
+ return result;
+ }
+
+
+ @Override
+ public Dimension getPreferredSize() {
+ Dimension preferredSize = super.getPreferredSize();
+ List stringList = autoChangeLine(width);
+ FontMetrics fontMetrics = this.getFontMetrics(this.getFont());
+ return new Dimension(preferredSize.width, fontMetrics.getHeight() * stringList.size());
+ }
+}
diff --git a/designer-base/src/main/java/com/fr/design/gui/style/BorderPane.java b/designer-base/src/main/java/com/fr/design/gui/style/BorderPane.java
index dcead1c878..3c917ea456 100644
--- a/designer-base/src/main/java/com/fr/design/gui/style/BorderPane.java
+++ b/designer-base/src/main/java/com/fr/design/gui/style/BorderPane.java
@@ -4,7 +4,6 @@ package com.fr.design.gui.style;
* Copyright(c) 2001-2010, FineReport Inc, All Rights Reserved.
*/
-import com.fr.base.BaseUtils;
import com.fr.base.CellBorderStyle;
import com.fr.base.Style;
import com.fr.design.constants.LayoutConstants;
@@ -17,8 +16,8 @@ import com.fr.design.gui.ilable.UILabel;
import com.fr.design.layout.TableLayout;
import com.fr.design.layout.TableLayoutHelper;
import com.fr.design.style.color.NewColorSelectBox;
-import com.fr.general.IOUtils;
import com.fr.design.utils.gui.AdjustWorkBookDefaultStyleUtils;
+import com.fr.general.IOUtils;
import com.fr.stable.Constants;
import com.fr.stable.CoreConstants;
@@ -206,6 +205,8 @@ public class BorderPane extends AbstractBasicStylePane implements GlobalNameObse
}
public void populateLineStyleAndColor(CellBorderStyle cellBorderStyle, boolean onlyInspectTop) {
+ resetLineStyleAndColorSetting();
+
if (cellBorderStyle.getTopStyle() != Constants.LINE_NONE) {
this.currentLineCombo.setSelectedLineStyle(cellBorderStyle.getTopStyle());
this.currentLineColorPane.setSelectObject(cellBorderStyle.getTopColor());
@@ -225,9 +226,6 @@ public class BorderPane extends AbstractBasicStylePane implements GlobalNameObse
} else if (cellBorderStyle.getHorizontalStyle() != Constants.LINE_NONE) {
this.currentLineCombo.setSelectedLineStyle(cellBorderStyle.getHorizontalStyle());
this.currentLineColorPane.setSelectObject(cellBorderStyle.getHorizontalColor());
- } else {
- this.currentLineCombo.setSelectedLineStyle(Constants.LINE_NONE);
- this.currentLineColorPane.setSelectObject(Color.BLACK);
}
}
@@ -236,6 +234,11 @@ public class BorderPane extends AbstractBasicStylePane implements GlobalNameObse
}
}
+ private void resetLineStyleAndColorSetting() {
+ this.currentLineCombo.setSelectedLineStyle(Constants.LINE_NONE);
+ this.currentLineColorPane.setSelectObject(null);
+ }
+
@Override
public Style update(Style style) {
diff --git a/designer-base/src/main/java/com/fr/design/javascript/JSContentPane.java b/designer-base/src/main/java/com/fr/design/javascript/JSContentPane.java
index 10e11c8fbb..10bee69d27 100644
--- a/designer-base/src/main/java/com/fr/design/javascript/JSContentPane.java
+++ b/designer-base/src/main/java/com/fr/design/javascript/JSContentPane.java
@@ -4,7 +4,9 @@ import com.fr.design.DesignerEnvManager;
import com.fr.design.border.UIRoundedBorder;
import com.fr.design.constants.KeyWords;
import com.fr.design.constants.UIConstants;
+import com.fr.design.dialog.BasicDialog;
import com.fr.design.dialog.BasicPane;
+import com.fr.design.dialog.DialogActionAdapter;
import com.fr.design.gui.autocomplete.AutoCompletion;
import com.fr.design.gui.autocomplete.BasicCompletion;
import com.fr.design.gui.autocomplete.CompletionProvider;
@@ -15,31 +17,145 @@ import com.fr.design.gui.ilable.UILabel;
import com.fr.design.gui.syntax.ui.rsyntaxtextarea.RSyntaxTextArea;
import com.fr.design.gui.syntax.ui.rsyntaxtextarea.SyntaxConstants;
import com.fr.design.javascript.beautify.JavaScriptFormatHelper;
+import com.fr.design.javascript.jsapi.JSImplPopulateAction;
+import com.fr.design.javascript.jsapi.JSImplUpdateAction;
import com.fr.design.layout.FRGUIPaneFactory;
+import com.fr.design.mainframe.DesignerContext;
import com.fr.general.IOUtils;
+import com.fr.js.JavaScriptImpl;
-import javax.swing.*;
-import java.awt.*;
+import java.awt.BorderLayout;
+import java.awt.Cursor;
+import java.awt.Dimension;
+import java.awt.FontMetrics;
import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.ArrayList;
+import javax.swing.JPanel;
+import javax.swing.KeyStroke;
+import javax.swing.SwingConstants;
+import javax.swing.SwingWorker;
public class JSContentPane extends BasicPane {
- private RSyntaxTextArea contentTextArea;
- private UILabel funNameLabel;
+ protected RSyntaxTextArea contentTextArea;
+ private UILabel funNameLabel = new UILabel();
private AutoCompletion ac;
private static final Dimension FUNCTION_NAME_LABEL_SIZE = new Dimension(300, 80);
-
+ private String[] defaultArgs;
private int titleWidth = 180;
+ private JPanel labelPane = new JPanel(new BorderLayout(6, 4));;
+ private NewJavaScriptImplPane newJavaScriptImplPane = null;
+ private JavaScriptImpl javaScript;
+ private JSImplUpdateAction jsImplUpdateAction;
+ private JSImplPopulateAction jsImplPopulateAction;
+ private boolean modal;
+ BasicDialog advancedEditorDialog ;
+ public JSContentPane(){}
public JSContentPane(String[] args) {
+ defaultArgs = args;
this.setLayout(FRGUIPaneFactory.createBorderLayout());
- funNameLabel = new UILabel();
- this.setFunctionTitle(args);
+ initFunctionTitle(args);
+
+ JPanel jsParaPane = createJSParaPane();
+ addNewPaneLabel();
+ this.add(jsParaPane, BorderLayout.NORTH);
+
+ UIScrollPane sp = createContentTextAreaPanel();
+ initContentTextAreaListener();
+ this.add(sp, BorderLayout.CENTER);
+
+ UILabel funNameLabel2 = new UILabel();
+ funNameLabel2.setText("}");
+ this.add(funNameLabel2, BorderLayout.SOUTH);
+ }
+ public JSContentPane(String[] args,boolean modal) {
+ this(args);
+ this.modal = modal;
+ }
+
+
+ public void setJsImplUpdateAction(JSImplUpdateAction jsImplUpdateAction){
+ this.jsImplUpdateAction = jsImplUpdateAction;
+ }
+
+ public void setJsImplPopulateAction(JSImplPopulateAction jsImplPopulateAction){
+ this.jsImplPopulateAction = jsImplPopulateAction;
+ }
+
+ public void updateJSImpl(JavaScriptImpl javaScript){
+ this.javaScript = javaScript;
+ }
+
+
+ private void addNewPaneLabel(){
+ UILabel advancedEditorLabel = new UILabel(com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Advanced_Editor"), SwingConstants.LEFT);
+ advancedEditorLabel.setCursor(new Cursor(Cursor.HAND_CURSOR));
+ advancedEditorLabel.addMouseListener(new MouseAdapter() {
+ @Override
+ public void mouseClicked(MouseEvent e) {
+ if(newJavaScriptImplPane == null){
+ newJavaScriptImplPane = new NewJavaScriptImplPane(defaultArgs);
+ }
+ jsImplUpdateAction.update(javaScript);
+ newJavaScriptImplPane.populate(javaScript);
+ if(advancedEditorDialog == null || !advancedEditorDialog.isVisible()) {
+ advancedEditorDialog = newJavaScriptImplPane.showWindow(DesignerContext.getDesignerFrame(), new DialogActionAdapter() {
+ @Override
+ public void doOk() {
+ if (javaScript != null) {
+ newJavaScriptImplPane.updateBean(javaScript);
+ jsImplPopulateAction.populate(javaScript);
+ }
+ }
+
+ @Override
+ public void doCancel() {
+ super.doCancel();
+ }
+ });
+ advancedEditorDialog.setModal(modal);
+ advancedEditorDialog.setResizable(true);
+ advancedEditorDialog.pack();
+ advancedEditorDialog.setVisible(true);
+ }
+ advancedEditorDialog.requestFocus();
+ }
+ });
+ labelPane.add(advancedEditorLabel,BorderLayout.CENTER);
+ }
+
+ protected UIScrollPane createContentTextAreaPanel(){
+ contentTextArea = new RSyntaxTextArea();
+ contentTextArea.setCloseCurlyBraces(true);
+ contentTextArea.setLineWrap(true);
+ contentTextArea.setSyntaxEditingStyle(SyntaxConstants.SYNTAX_STYLE_JAVASCRIPT);
+ contentTextArea.setCodeFoldingEnabled(true);
+ contentTextArea.setAntiAliasingEnabled(true);
+ return new UIScrollPane(contentTextArea);
+ }
+
+ private void initContentTextAreaListener(){
+ contentTextArea.addFocusListener(new FocusListener() {
+ @Override
+ public void focusGained(FocusEvent e) {
+ // 获得焦点时 安装
+ installAutoCompletion();
+ }
+
+ @Override
+ public void focusLost(FocusEvent e) {
+ // 失去焦点时 卸载
+ uninstallAutoCompletion();
+ }
+ });
+ }
+
+ protected JPanel createJSParaPane(){
UILabel label = new UILabel(com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Basic_Format_JavaScript"), IOUtils.readIcon("com/fr/design/images/edit/format.png"), SwingConstants.LEFT);
label.setCursor(new Cursor(Cursor.HAND_CURSOR));
label.setToolTipText(com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Basic_Format_JavaScript"));
@@ -65,43 +181,20 @@ public class JSContentPane extends BasicPane {
}
});
- //REPORT-10533 用户参数多达25个,导致JS没地方写,增加滚动条显示
+ labelPane.add(label,BorderLayout.EAST);
JPanel jsParaPane = new JPanel(new BorderLayout(4, 4));
jsParaPane.setPreferredSize(new Dimension(300, 80));
UIScrollPane scrollPane = new UIScrollPane(funNameLabel);
scrollPane.setPreferredSize(FUNCTION_NAME_LABEL_SIZE);
scrollPane.setBorder(new UIRoundedBorder(UIConstants.TITLED_BORDER_COLOR, 1, UIConstants.ARC));
jsParaPane.add(scrollPane, BorderLayout.WEST);
- jsParaPane.add(label, BorderLayout.EAST);
- this.add(jsParaPane, BorderLayout.NORTH);
-
- contentTextArea = new RSyntaxTextArea();
- contentTextArea.setCloseCurlyBraces(true);
- contentTextArea.setLineWrap(true);
- contentTextArea.setSyntaxEditingStyle(SyntaxConstants.SYNTAX_STYLE_JAVASCRIPT);
- contentTextArea.setCodeFoldingEnabled(true);
- contentTextArea.setAntiAliasingEnabled(true);
-
- UIScrollPane sp = new UIScrollPane(contentTextArea);
- this.add(sp, BorderLayout.CENTER);
-
- contentTextArea.addFocusListener(new FocusListener() {
- @Override
- public void focusGained(FocusEvent e) {
- // 获得焦点时 安装
- installAutoCompletion();
- }
-
- @Override
- public void focusLost(FocusEvent e) {
- // 失去焦点时 卸载
- uninstallAutoCompletion();
- }
- });
+ jsParaPane.add(labelPane, BorderLayout.EAST);
+ return jsParaPane;
+ }
- UILabel funNameLabel2 = new UILabel();
- funNameLabel2.setText("}");
- this.add(funNameLabel2, BorderLayout.SOUTH);
+ protected void initFunctionTitle(String[] args){
+ funNameLabel = new UILabel();
+ this.setFunctionTitle(args);
}
private KeyStroke convert2KeyStroke(String ks) {
diff --git a/designer-base/src/main/java/com/fr/design/javascript/JSContentWithDescriptionPane.java b/designer-base/src/main/java/com/fr/design/javascript/JSContentWithDescriptionPane.java
new file mode 100644
index 0000000000..b1516fe366
--- /dev/null
+++ b/designer-base/src/main/java/com/fr/design/javascript/JSContentWithDescriptionPane.java
@@ -0,0 +1,858 @@
+package com.fr.design.javascript;
+
+import com.fr.design.border.UIRoundedBorder;
+import com.fr.design.constants.UIConstants;
+import com.fr.design.gui.autocomplete.AutoCompleteExtraRefreshComponent;
+import com.fr.design.gui.autocomplete.BasicCompletion;
+import com.fr.design.gui.autocomplete.CompletionCellRenderer;
+import com.fr.design.gui.autocomplete.CompletionProvider;
+import com.fr.design.gui.autocomplete.DefaultCompletionProvider;
+import com.fr.design.gui.autocomplete.JSImplPaneAutoCompletion;
+import com.fr.design.gui.ibutton.UIButton;
+import com.fr.design.gui.icontainer.UIScrollPane;
+import com.fr.design.gui.ilable.UILabel;
+import com.fr.design.gui.itextarea.UITextArea;
+import com.fr.design.gui.itextfield.UITextField;
+import com.fr.design.i18n.Toolkit;
+import com.fr.design.javascript.jsapi.JSAPITreeHelper;
+import com.fr.design.javascript.jsapi.JSAPIUserObject;
+import com.fr.design.layout.FRGUIPaneFactory;
+import com.fr.general.CloudCenter;
+import com.fr.general.ComparatorUtils;
+import com.fr.general.http.HttpToolbox;
+import com.fr.json.JSONArray;
+import com.fr.json.JSONException;
+import com.fr.json.JSONObject;
+import com.fr.log.FineLoggerFactory;
+import com.fr.stable.StringUtils;
+import java.awt.BorderLayout;
+import java.awt.Color;
+import java.awt.Component;
+import java.awt.Desktop;
+import java.awt.Dimension;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.FocusAdapter;
+import java.awt.event.FocusEvent;
+import java.awt.event.KeyAdapter;
+import java.awt.event.KeyEvent;
+import java.awt.event.KeyListener;
+import java.awt.event.MouseAdapter;
+import java.awt.event.MouseEvent;
+import java.awt.event.MouseListener;
+import java.io.IOException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+import javax.swing.BorderFactory;
+import javax.swing.DefaultListCellRenderer;
+import javax.swing.DefaultListModel;
+import javax.swing.JComponent;
+import javax.swing.JList;
+import javax.swing.JPanel;
+import javax.swing.JPopupMenu;
+import javax.swing.JScrollPane;
+import javax.swing.JTree;
+import javax.swing.event.ListSelectionEvent;
+import javax.swing.event.ListSelectionListener;
+import javax.swing.event.TreeSelectionEvent;
+import javax.swing.event.TreeSelectionListener;
+import javax.swing.tree.DefaultMutableTreeNode;
+import javax.swing.tree.DefaultTreeCellRenderer;
+import javax.swing.tree.DefaultTreeModel;
+import javax.swing.tree.TreeNode;
+import javax.swing.tree.TreePath;
+
+public class JSContentWithDescriptionPane extends JSContentPane implements KeyListener {
+
+ //搜索关键词输入框
+ private UITextField keyWordTextField = new UITextField(16);
+ //搜索出的提示列表
+ private JList tipsList;
+ private DefaultListModel tipsListModel = new DefaultListModel();
+
+ private JList interfaceNameList;
+ private DefaultListModel interfaceNameModel;
+
+ private JTree moduleTree;
+
+ private DefaultCompletionProvider completionProvider;
+ private JSImplPaneAutoCompletion autoCompletion;
+
+ //函数说明文本框
+ private UITextArea descriptionTextArea;
+
+ private JPopupMenu popupMenu;
+
+ private InterfaceAndDescriptionPanel interfaceAndDescriptionPanel;
+ private JList helpDOCList;
+
+ private int ifHasBeenWriten = 0;
+ private int currentPosition = 0;
+ private int beginPosition = 0;
+ private int insertPosition = 0;
+ private static final String SEPARATOR = "_";
+
+ private static final int KEY_10 = 10;
+ //上下左右
+ private static final int KEY_37 = 37;
+ private static final int KEY_38 = 38;
+ private static final int KEY_39 = 39;
+ private static final int KEY_40 = 40;
+
+ private static final String URL_FOR_TEST_NETWORK = "https://www.baidu.com";
+
+ private static final String DOCUMENT_SEARCH_URL = CloudCenter.getInstance().acquireUrlByKind("af.doc_search");
+
+ private String currentValue;
+
+ public JSContentWithDescriptionPane(String[] args) {
+ this.setLayout(new BorderLayout());
+ //===============================
+ this.initFunctionTitle(args);
+ JPanel jsParaAndSearchPane = new JPanel(new BorderLayout());
+
+ //js函数声明面板
+ JPanel jsParaPane = createJSParaPane();
+
+ jsParaPane.setPreferredSize(new Dimension(650, 80));
+ //右上角的搜索提示面板
+ JPanel tipsPane = createTipsPane();
+
+ jsParaAndSearchPane.add(jsParaPane, BorderLayout.CENTER);
+ jsParaAndSearchPane.add(tipsPane, BorderLayout.EAST);
+
+ initPopTips();
+
+ //js文本编辑面板
+ UIScrollPane contentTextAreaPanel = createContentTextAreaPanel();
+ initContextAreaListener();
+
+ contentTextAreaPanel.setPreferredSize(new Dimension(850, 250));
+ //js函数结束标签
+ UILabel endBracketsLabel = new UILabel();
+ endBracketsLabel.setText("}");
+
+ //结尾括号和复用函数按钮面板
+ JPanel endBracketsPanel = FRGUIPaneFactory.createBorderLayout_S_Pane();
+ endBracketsPanel.add(endBracketsLabel, BorderLayout.WEST);
+
+ JPanel northPanel = FRGUIPaneFactory.createBorderLayout_S_Pane();
+ northPanel.add(jsParaAndSearchPane, BorderLayout.NORTH);
+
+ northPanel.add(contentTextAreaPanel, BorderLayout.CENTER);
+ northPanel.add(endBracketsPanel, BorderLayout.SOUTH);
+
+ //主编辑框,也就是面板的正中间部分
+ this.add(northPanel, BorderLayout.CENTER);
+
+ //函数分类和函数说明面板==================================
+ JPanel functionNameAndDescriptionPanel = createInterfaceAndDescriptionPanel();
+ functionNameAndDescriptionPanel.setPreferredSize(new Dimension(880, 220));
+
+ this.add(functionNameAndDescriptionPanel, BorderLayout.SOUTH);
+ }
+
+ private void initContextAreaListener() {
+ contentTextArea.addKeyListener(new KeyAdapter() {
+ @Override
+ public void keyTyped(KeyEvent e) {
+ if ((e.getKeyChar() >= 'A' && e.getKeyChar() <= 'z') || e.getKeyChar() == '_') {
+ if (autoCompletion != null) {
+ autoCompletion.doCompletion();
+ }
+ }
+ }
+
+ @Override
+ public void keyReleased(KeyEvent e) {
+ contentTextArea.setForeground(Color.black);
+ }
+ });
+ contentTextArea.addKeyListener(this);
+ contentTextArea.addFocusListener(new FocusAdapter() {
+ @Override
+ public void focusGained(FocusEvent e) {
+ if (autoCompletion == null) {
+ installAutoCompletion();
+ }
+ }
+
+ @Override
+ public void focusLost(FocusEvent e) {
+ uninstallAutoCompletion();
+ }
+ });
+ }
+
+ @Override
+ public void keyTyped(KeyEvent e) {
+
+ }
+
+ @Override
+ public void keyPressed(KeyEvent e) {
+ if (ifHasBeenWriten == 0) {
+ this.contentTextArea.setText(StringUtils.EMPTY);
+ }
+ }
+
+ @Override
+ public void keyReleased(KeyEvent e) {
+ int key = e.getKeyCode();
+ if (key == KEY_38 || key == KEY_40 || key == KEY_37 || key == KEY_39 || key == KEY_10) //如果是删除符号 ,为了可读性 没有和其他按键的程序相融合
+ {
+ currentPosition = contentTextArea.getCaretPosition();
+ insertPosition = currentPosition;
+ beginPosition = getBeginPosition();
+ } else {
+ if (contentTextArea.getText().trim().length() == 0) {
+ insertPosition = 0;
+ } else {
+ contentTextArea.setForeground(Color.black);
+ currentPosition = contentTextArea.getCaretPosition();
+ beginPosition = getBeginPosition();
+ insertPosition = beginPosition;
+ ifHasBeenWriten = 1;
+ }
+ }
+ }
+
+ private int getBeginPosition() {
+ int i = currentPosition;
+ String textArea = contentTextArea.getText();
+ for (; i > 0; i--) {
+ String tested = textArea.substring(i - 1, i).toUpperCase();
+ char[] testedChar = tested.toCharArray();
+ if (isChar(testedChar[0]) || isNum(testedChar[0])) {
+ continue;
+ } else {
+ break;
+ }
+ }
+ return i;
+ }
+
+ private static boolean isNum(char tested) {
+ return tested >= '0' && tested <= '9';
+ }
+
+ private boolean isChar(char tested) {
+ return tested >= 'A' && tested <= 'Z' || tested >= 'a' && tested < 'z';
+ }
+
+ public class InterfaceAndDescriptionPanel extends JPanel implements AutoCompleteExtraRefreshComponent {
+ @Override
+ public void refresh(String replacementText) {
+ fixInterfaceNameList(replacementText);
+ }
+ }
+
+ private void fixInterfaceNameList(String interfaceName) {
+ DefaultTreeModel defaultTreeModel = (DefaultTreeModel) moduleTree.getModel();
+ TreeNode root = (TreeNode) defaultTreeModel.getRoot();
+ String directCategory = JSAPITreeHelper.getDirectCategory(interfaceName);
+ if (directCategory == null) {
+ return;
+ }
+ setModuleTreeSelection(root, directCategory, defaultTreeModel);
+ interfaceNameModel = (DefaultListModel) interfaceNameList.getModel();
+ interfaceNameModel.clear();
+ List interfaceNames = JSAPITreeHelper.getNames(directCategory);
+ int index = 0;
+ for (int i = 0; i < interfaceNames.size(); i++) {
+ interfaceNameModel.addElement(interfaceNames.get(i));
+ if (StringUtils.equals(interfaceNames.get(i), interfaceName)) {
+ index = i;
+ }
+ }
+ interfaceNameList.setSelectedIndex(index);
+ interfaceNameList.ensureIndexIsVisible(index);
+ }
+
+ private boolean setModuleTreeSelection(TreeNode node, String directCategory, DefaultTreeModel treeModel) {
+
+ DefaultMutableTreeNode defaultMutableTreeNode = (DefaultMutableTreeNode) node;
+ Object userObject = defaultMutableTreeNode.getUserObject();
+ if (userObject instanceof JSAPIUserObject) {
+ String value = ((JSAPIUserObject) userObject).getValue();
+ if (StringUtils.equals(value, directCategory)) {
+ moduleTree.setSelectionPath(new TreePath(treeModel.getPathToRoot(node)));
+ return true;
+ }
+ return false;
+ }
+ for (int i = 0; i < node.getChildCount(); i++) {
+ if (setModuleTreeSelection(node.getChildAt(i), directCategory, treeModel)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private JPanel createInterfaceAndDescriptionPanel() {
+ interfaceAndDescriptionPanel = new InterfaceAndDescriptionPanel();
+ interfaceAndDescriptionPanel.setLayout(new BorderLayout(4, 4));
+ JPanel interfacePanel = new JPanel(new BorderLayout(4, 4));
+ interfaceAndDescriptionPanel.add(interfacePanel, BorderLayout.WEST);
+ JPanel descriptionAndDocumentPanel = new JPanel(new BorderLayout(4, 4));
+ //函数说明和帮助文档框
+ initDescriptionArea(descriptionAndDocumentPanel);
+
+ //模块和接口面板
+ initInterfaceModuleTree(interfacePanel);
+ initInterfaceNameList(interfacePanel);
+
+ initHelpDocumentPane(descriptionAndDocumentPanel);
+
+ interfaceAndDescriptionPanel.add(descriptionAndDocumentPanel, BorderLayout.CENTER);
+ return interfaceAndDescriptionPanel;
+ }
+
+ private void doHelpDocumentSearch() {
+ Object value = interfaceNameList.getSelectedValue();
+ if (value != null) {
+ String url = DOCUMENT_SEARCH_URL + value.toString();
+ try {
+ String result = HttpToolbox.get(url);
+ JSONObject jsonObject = new JSONObject(result);
+ JSONArray jsonArray = jsonObject.optJSONArray("list");
+ if (jsonArray != null) {
+ DefaultListModel helpDOCModel = (DefaultListModel) helpDOCList.getModel();
+ helpDOCModel.clear();
+ for (int i = 0; i < jsonArray.length(); i++) {
+ JSONObject resultJSONObject = jsonArray.optJSONObject(i);
+ String docURL = resultJSONObject.optString("url");
+ String name = resultJSONObject.optString("title").trim();
+ HelpDocument helpDocument = new HelpDocument(docURL, name);
+ helpDOCModel.addElement(helpDocument);
+ }
+ }
+ } catch (JSONException e) {
+ FineLoggerFactory.getLogger().debug(e.getMessage(), e);
+ } catch (Exception e) {
+ FineLoggerFactory.getLogger().error(e.getMessage(), e);
+ }
+ }
+ }
+
+ private void initHelpDocumentPane(JPanel descriptionAndDocumentPanel) {
+ UIScrollPane helpDOCScrollPane;
+ if (isNetworkOk()) {
+ helpDOCList = new JList(new DefaultListModel());
+ initHelpDOCListRender();
+ initHelpDOCListListener();
+ helpDOCScrollPane = new UIScrollPane(helpDOCList);
+ doHelpDocumentSearch();
+ } else {
+ UILabel label1 = new UILabel(Toolkit.i18nText("Fine-Design_Net_Connect_Failed"), 0);
+ label1.setPreferredSize(new Dimension(180, 20));
+ UILabel label2 = new UILabel(Toolkit.i18nText("Fine-Design_Basic_Reload"), 0);
+ label2.setPreferredSize(new Dimension(180, 20));
+ label2.setForeground(Color.blue);
+ JPanel labelPane = FRGUIPaneFactory.createVerticalFlowLayout_Pane(true, 0, 0, 0);
+ labelPane.setBackground(Color.WHITE);
+ labelPane.add(label1);
+ labelPane.add(label2);
+ JPanel containerPanel = FRGUIPaneFactory.createBorderLayout_S_Pane();
+ containerPanel.add(labelPane, BorderLayout.CENTER);
+ helpDOCScrollPane = new UIScrollPane(containerPanel);
+ label2.addMouseListener(new MouseAdapter() {
+ @Override
+ public void mouseClicked(MouseEvent e) {
+ descriptionAndDocumentPanel.removeAll();
+ initHelpDocumentPane(descriptionAndDocumentPanel);
+
+ }
+ });
+ }
+ helpDOCScrollPane.setPreferredSize(new Dimension(200, 200));
+ helpDOCScrollPane.setBorder(null);
+ descriptionAndDocumentPanel.add(this.createNamePane(Toolkit.i18nText("Fine-Design_Relevant_Cases"), helpDOCScrollPane), BorderLayout.EAST);
+
+ }
+
+ private void initHelpDOCListListener() {
+ helpDOCList.addMouseListener(new MouseAdapter() {
+ @Override
+ public void mouseClicked(MouseEvent e) {
+ if (e.getClickCount() == 2) {
+ Object value = helpDOCList.getSelectedValue();
+ if (value instanceof HelpDocument) {
+ String url = ((HelpDocument) value).getDocumentUrl();
+ try {
+ Desktop.getDesktop().browse(new URI(url));
+ } catch (IOException ex) {
+ FineLoggerFactory.getLogger().error(ex.getMessage(), ex);
+ } catch (URISyntaxException ex) {
+ FineLoggerFactory.getLogger().error(ex.getMessage(), ex);
+ }
+ }
+ }
+ }
+ });
+ }
+
+ private void initHelpDOCListRender() {
+ helpDOCList.setCellRenderer(new DefaultListCellRenderer() {
+ @Override
+ public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
+ super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
+ if (value instanceof HelpDocument) {
+ this.setText(((HelpDocument) value).getName());
+ this.setForeground(Color.BLUE);
+ }
+ return this;
+ }
+ });
+ }
+
+ private static boolean isNetworkOk() {
+ try {
+ HttpToolbox.get(URL_FOR_TEST_NETWORK);
+ return true;
+ } catch (Exception ignore) {
+ // 网络异常
+ return false;
+ }
+ }
+
+ private static class HelpDocument {
+ private String documentUrl;
+
+
+ private String name;
+
+ public HelpDocument(String documentUrl, String name) {
+ this.documentUrl = documentUrl;
+ this.name = name;
+ }
+
+ public String getDocumentUrl() {
+ return documentUrl;
+ }
+
+ public String getName() {
+ return name;
+ }
+ }
+
+ private void initDescriptionArea(JPanel descriptionPanel) {
+ descriptionTextArea = new UITextArea();
+ UIScrollPane descriptionScrollPane = new UIScrollPane(descriptionTextArea);
+ descriptionScrollPane.setPreferredSize(new Dimension(300, 200));
+ descriptionPanel.add(this.createNamePane(Toolkit.i18nText("Fine-Design_Interface_Description"), descriptionScrollPane), BorderLayout.CENTER);
+ descriptionTextArea.setBackground(Color.white);
+ descriptionTextArea.setLineWrap(true);
+ descriptionTextArea.setWrapStyleWord(true);
+ descriptionTextArea.setEditable(false);
+ }
+
+ private void installAutoCompletion() {
+ CompletionProvider provider = createCompletionProvider();
+ autoCompletion = new JSImplPaneAutoCompletion(provider);
+ autoCompletion.setListCellRenderer(new CompletionCellRenderer());
+ autoCompletion.install(contentTextArea);
+ autoCompletion.installExtraRefreshComponent(interfaceAndDescriptionPanel);
+ }
+
+ private void uninstallAutoCompletion() {
+ if (autoCompletion != null) {
+ autoCompletion.uninstall();
+ autoCompletion = null;
+ }
+ }
+
+ private CompletionProvider createCompletionProvider() {
+ if (completionProvider == null) {
+ completionProvider = new DefaultCompletionProvider();
+ for (String name : JSAPITreeHelper.getAllNames()) {
+ completionProvider.addCompletion(new BasicCompletion(completionProvider, name));
+ }
+ }
+ return completionProvider;
+ }
+
+ private void initInterfaceModuleTree(JPanel interfacePanel) {
+ moduleTree = new JTree();
+ UIScrollPane moduleTreePane = new UIScrollPane(moduleTree);
+ moduleTreePane.setBorder(new UIRoundedBorder(UIConstants.LINE_COLOR, 1, UIConstants.ARC));
+ interfacePanel.add(this.createNamePane(Toolkit.i18nText("Fine-Design_Module"), moduleTreePane), BorderLayout.WEST);
+ moduleTreePane.setPreferredSize(new Dimension(180, 200));
+
+ moduleTree.setRootVisible(false);
+ moduleTree.setShowsRootHandles(true);
+ moduleTree.setCellRenderer(moduleTreeCellRender);
+ DefaultTreeModel moduleTreeModel = (DefaultTreeModel) moduleTree.getModel();
+ DefaultMutableTreeNode rootNode = (DefaultMutableTreeNode) moduleTreeModel.getRoot();
+ rootNode.removeAllChildren();
+
+ JSAPITreeHelper.createJSAPITree(rootNode);
+ moduleTreeModel.reload();
+
+ initModuleTreeSelectionListener();
+ }
+
+ private void initModuleTreeSelectionListener() {
+ moduleTree.addTreeSelectionListener(new TreeSelectionListener() {
+ @Override
+ public void valueChanged(TreeSelectionEvent e) {
+ DefaultMutableTreeNode selectedTreeNode = (DefaultMutableTreeNode) moduleTree.getLastSelectedPathComponent();
+ Object selectedValue = selectedTreeNode.getUserObject();
+ if (null == selectedValue) {
+ return;
+ }
+ if (selectedValue instanceof JSAPIUserObject) {
+ interfaceNameModel = (DefaultListModel) interfaceNameList.getModel();
+ interfaceNameModel.clear();
+ String text = ((JSAPIUserObject) selectedValue).getValue();
+ List allInterfaceNames = JSAPITreeHelper.getNames(text);
+ for (String interfaceName : allInterfaceNames) {
+ interfaceNameModel.addElement(interfaceName);
+ }
+ if (interfaceNameModel.size() > 0) {
+ interfaceNameList.setSelectedIndex(0);
+ setDescription(interfaceNameList.getSelectedValue().toString());
+ interfaceNameList.ensureIndexIsVisible(0);
+ }
+ }
+ }
+ });
+ }
+
+
+ private DefaultTreeCellRenderer moduleTreeCellRender = new DefaultTreeCellRenderer() {
+ public Component getTreeCellRendererComponent(JTree tree,
+ Object value, boolean selected, boolean expanded,
+ boolean leaf, int row, boolean hasFocus) {
+ super.getTreeCellRendererComponent(tree, value, selected,
+ expanded, leaf, row, hasFocus);
+
+ DefaultMutableTreeNode treeNode = (DefaultMutableTreeNode) value;
+ Object userObj = treeNode.getUserObject();
+ if (userObj instanceof JSAPIUserObject) {
+ this.setText(((JSAPIUserObject) userObj).getDisplayText());
+ this.setIcon(null);
+ }
+ return this;
+ }
+
+ };
+
+
+ private void initInterfaceNameList(JPanel interfacePanel) {
+ interfaceNameList = new JList(new DefaultListModel());
+ UIScrollPane interfaceNamePanelScrollPane = new UIScrollPane(interfaceNameList);
+ interfaceNamePanelScrollPane.setPreferredSize(new Dimension(180, 200));
+ interfacePanel.add(
+ this.createNamePane(Toolkit.i18nText("Fine-Design_Interface") + ":", interfaceNamePanelScrollPane),
+ BorderLayout.CENTER);
+
+ interfaceNamePanelScrollPane.setBorder(new UIRoundedBorder(UIConstants.LINE_COLOR, 1, UIConstants.ARC));
+ initInterfaceNameModule();
+ initInterfaceNameListSelectionListener();
+ initInterfaceNameListMouseListener();
+ }
+
+ private void initInterfaceNameModule() {
+ moduleTree.setSelectionPath(moduleTree.getPathForRow(0));
+ }
+
+ private void setDescription(String interfaceName) {
+ StringBuilder il8Key = new StringBuilder();
+ moduleTree.getSelectionPath().getPath();
+ Object obj = moduleTree.getSelectionPath().getPath()[moduleTree.getSelectionPath().getPath().length - 1];
+ Object userObject = ((DefaultMutableTreeNode) obj).getUserObject();
+ if (userObject instanceof JSAPIUserObject) {
+ il8Key.append(JSAPITreeHelper.getDirectCategory(interfaceName));
+ }
+ interfaceName = interfaceName.toUpperCase();
+ if (!interfaceName.startsWith(SEPARATOR)) {
+ interfaceName = SEPARATOR + interfaceName;
+ }
+ il8Key.append(interfaceName);
+ descriptionTextArea.setText(Toolkit.i18nText(il8Key.toString()));
+ descriptionTextArea.moveCaretPosition(0);
+ }
+
+ private void initInterfaceNameListSelectionListener() {
+ interfaceNameList.addListSelectionListener(new ListSelectionListener() {
+
+ public void valueChanged(ListSelectionEvent evt) {
+ Object selectedValue = interfaceNameList.getSelectedValue();
+ if (selectedValue == null) {
+ return;
+ }
+ String interfaceName = selectedValue.toString();
+ if (!StringUtils.equals(interfaceName, currentValue)) {
+ setDescription(interfaceName);
+ doHelpDocumentSearch();
+ currentValue = interfaceName;
+ }
+
+ }
+ });
+ }
+
+
+ private void initInterfaceNameListMouseListener() {
+ interfaceNameList.addMouseListener(new MouseAdapter() {
+ public void mouseClicked(MouseEvent evt) {
+ if (evt.getClickCount() >= 2) {
+ Object selectedValue = interfaceNameList.getSelectedValue();
+ String interfaceName = selectedValue.toString();
+ applyText(interfaceName);
+ }
+ }
+ });
+ }
+
+ private void applyText(String text) {
+ if (text == null || text.length() <= 0) {
+ return;
+ }
+ if (ifHasBeenWriten == 0) {
+ contentTextArea.setForeground(Color.black);
+ contentTextArea.setText(StringUtils.EMPTY);
+ ifHasBeenWriten = 1;
+ insertPosition = 0;
+ }
+ String textAll = contentTextArea.getText();
+ currentPosition = contentTextArea.getCaretPosition();
+ int insert = 0;
+ int current = 0;
+ if (insertPosition <= currentPosition) {
+ insert = insertPosition;
+ current = currentPosition;
+ } else {
+ insert = currentPosition;
+ current = insertPosition;
+ }
+ String beforeIndexOfInsertString = textAll.substring(0, insert);
+ String afterIndexofInsertString = textAll.substring(current);
+ contentTextArea.setText(beforeIndexOfInsertString + text + afterIndexofInsertString);
+ contentTextArea.getText();
+ contentTextArea.requestFocus();
+ insertPosition = contentTextArea.getCaretPosition();
+ }
+
+ private JPanel createNamePane(String name, JComponent comp) {
+ JPanel namePane = new JPanel(new BorderLayout(4, 4));
+ namePane.add(new UILabel(name), BorderLayout.NORTH);
+ namePane.add(comp, BorderLayout.CENTER);
+ return namePane;
+ }
+
+ private JPanel createTipsPane() {
+ JPanel tipsPane = FRGUIPaneFactory.createBorderLayout_S_Pane();
+ tipsPane.setLayout(new BorderLayout(4, 4));
+ tipsPane.setBorder(BorderFactory.createEmptyBorder(30, 2, 0, 0));
+ JPanel searchPane = FRGUIPaneFactory.createBorderLayout_S_Pane();
+ searchPane.setLayout(new BorderLayout(4, 4));
+ searchPane.add(keyWordTextField, BorderLayout.CENTER);
+
+ //搜索按钮
+ UIButton searchButton = new UIButton(Toolkit.i18nText("Fine-Design_Basic_Search"));
+ searchButton.addActionListener(new ActionListener() {
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ String toFind = keyWordTextField.getText();
+ search(toFind);
+ popTips();
+ tipsList.requestFocusInWindow();
+ }
+ });
+
+ keyWordTextField.addKeyListener(new KeyAdapter() {
+ @Override
+ public void keyPressed(KeyEvent e) {
+ if (e.getKeyChar() == KeyEvent.VK_ENTER) {
+ e.consume();
+ String toFind = keyWordTextField.getText();
+ search(toFind);
+ popTips();
+ tipsList.requestFocusInWindow();
+ }
+ }
+ });
+
+ searchPane.add(searchButton, BorderLayout.EAST);
+ tipsPane.add(searchPane, BorderLayout.NORTH);
+
+ tipsList = new JList(tipsListModel);
+ tipsList.addMouseListener(tipsListMouseListener);
+ tipsList.addListSelectionListener(tipsListSelectionListener);
+ tipsList.addKeyListener(tipListKeyListener);
+
+ return tipsPane;
+ }
+
+ private void search(String key) {
+ tipsListModel.removeAllElements();
+ tipsListModel.clear();
+ key = key.replaceAll(StringUtils.BLANK, StringUtils.EMPTY);
+ ArrayList list = new ArrayList<>();
+ if (!StringUtils.isEmpty(key)) {
+ List allNames = JSAPITreeHelper.getAllNames();
+ for (String name : allNames) {
+ if (searchResult(key, name)) {
+ list.add(name);
+ }
+ }
+ String finalKey = key;
+ Collections.sort(list, new Comparator() {
+ @Override
+ public int compare(String o1, String o2) {
+ int result;
+ boolean o1StartWidth = o1.toLowerCase().startsWith(finalKey.toLowerCase());
+ boolean o2StartWidth = o2.toLowerCase().startsWith(finalKey.toLowerCase());
+ if (o1StartWidth) {
+ result = o2StartWidth ? o1.compareTo(o2) : -1;
+ } else {
+ result = o2StartWidth ? 1 : o1.compareTo(o2);
+ }
+ return result;
+ }
+ });
+ for (String name : list) {
+ tipsListModel.addElement(name);
+ }
+ if (!tipsListModel.isEmpty()) {
+ tipsList.setSelectedIndex(0);
+ }
+ }
+ }
+
+ private boolean searchResult(String key, String interfaceName) {
+ if (StringUtils.isBlank(key) || StringUtils.isBlank(interfaceName)) {
+ return false;
+ }
+ int length = key.length();
+ String temp = interfaceName.toUpperCase();
+ for (int i = 0; i < length; i++) {
+ String check = key.substring(i, i + 1);
+ int index = temp.indexOf(check.toUpperCase());
+ if (index == -1) {
+ return false;
+ } else {
+ temp = temp.substring(index + 1);
+ }
+ }
+ return true;
+ }
+
+ private void initPopTips() {
+ popupMenu = new JPopupMenu();
+ JScrollPane tipsScrollPane = new JScrollPane(tipsList);
+ popupMenu.add(tipsScrollPane);
+ tipsScrollPane.setPreferredSize(new Dimension(220, 146));
+ tipsScrollPane.setBorder(new UIRoundedBorder(UIConstants.LINE_COLOR, 1, UIConstants.ARC));
+ }
+
+ private void popTips() {
+ popupMenu.show(keyWordTextField, 0, 23);
+ }
+
+ private ListSelectionListener tipsListSelectionListener = new ListSelectionListener() {
+ @Override
+ public void valueChanged(ListSelectionEvent e) {
+ Object selectValue = tipsList.getSelectedValue();
+ if (selectValue == null) {
+ return;
+ }
+ String interfaceName = selectValue.toString();
+ fixInterfaceNameList(interfaceName);
+ }
+ };
+
+ private KeyListener tipListKeyListener = new KeyAdapter() {
+ @Override
+ public void keyPressed(KeyEvent e) {
+ if (e.getKeyChar() == KeyEvent.VK_ENTER) {
+ Object selectValue = tipsList.getSelectedValue();
+ if (selectValue == null) {
+ return;
+ }
+ tipListValueSelectAction(selectValue.toString());
+ if (popupMenu != null) {
+ popupMenu.setVisible(false);
+ }
+ contentTextArea.requestFocusInWindow();
+ }
+ }
+ };
+
+ private void tipListValueSelectAction(String value) {
+ if (ifHasBeenWriten == 0) {
+ contentTextArea.setForeground(Color.black);
+ contentTextArea.setText(StringUtils.EMPTY);
+ }
+ contentTextArea.setForeground(Color.black);
+ currentPosition = contentTextArea.getCaretPosition();
+ String output = value;
+ String textAll = contentTextArea.getText();
+ String textReplaced;
+ int position = 0;
+ if (insertPosition <= currentPosition) {
+ textReplaced = textAll.substring(0, insertPosition) + output + textAll.substring(currentPosition);
+ position = insertPosition + output.length();
+ } else {
+ textReplaced = textAll.substring(0, currentPosition) + output + textAll.substring(insertPosition);
+ position = currentPosition + output.length();
+ }
+ contentTextArea.setText(textReplaced);
+ contentTextArea.setCaretPosition(position);
+ insertPosition = position;
+ ifHasBeenWriten = 1;
+ tipsListModel.removeAllElements();
+ }
+
+ private MouseListener tipsListMouseListener = new MouseAdapter() {
+ String singlePressContent;
+
+ String doublePressContent;
+
+ @Override
+ public void mousePressed(MouseEvent e) {
+ int index = tipsList.getSelectedIndex();
+ if (index != -1) {
+ if (e.getClickCount() == 1) {
+ singlePressContent = (String) tipsListModel.getElementAt(index);
+ } else if (e.getClickCount() == 2) {
+ doublePressContent = (String) tipsListModel.getElementAt(index);
+ }
+ }
+ }
+
+ @Override
+ public void mouseReleased(MouseEvent e) {
+ int index = tipsList.getSelectedIndex();
+ if (index != -1) {
+ if (e.getClickCount() == 1) {
+ if (ComparatorUtils.equals((String) tipsListModel.getElementAt(index), singlePressContent)) {
+ singleClickActuator(singlePressContent);
+ }
+ } else if (e.getClickCount() == 2) {
+ if (ComparatorUtils.equals((String) tipsListModel.getElementAt(index), doublePressContent)) {
+ doubleClickActuator(doublePressContent);
+ }
+ if (popupMenu != null) {
+ popupMenu.setVisible(false);
+ }
+ }
+ }
+ }
+
+ private void singleClickActuator(String currentLineContent) {
+ setDescription(currentLineContent);
+ fixInterfaceNameList(currentLineContent);
+ }
+
+ private void doubleClickActuator(String currentLineContent) {
+ tipListValueSelectAction(currentLineContent);
+ }
+ };
+}
diff --git a/designer-base/src/main/java/com/fr/design/javascript/JavaScriptImplPane.java b/designer-base/src/main/java/com/fr/design/javascript/JavaScriptImplPane.java
index 158c05d1e1..6a83ad3d24 100644
--- a/designer-base/src/main/java/com/fr/design/javascript/JavaScriptImplPane.java
+++ b/designer-base/src/main/java/com/fr/design/javascript/JavaScriptImplPane.java
@@ -9,6 +9,8 @@ import com.fr.design.gui.itableeditorpane.UITableEditAction;
import com.fr.design.gui.itableeditorpane.UITableEditorPane;
import com.fr.design.gui.itextfield.UITextField;
import com.fr.design.hyperlink.AbstractHyperLinkPane;
+import com.fr.design.javascript.jsapi.JSImplPopulateAction;
+import com.fr.design.javascript.jsapi.JSImplUpdateAction;
import com.fr.design.mainframe.DesignerContext;
import com.fr.design.scrollruler.ModLineBorder;
import com.fr.design.utils.gui.GUICoreUtils;
@@ -17,10 +19,16 @@ import com.fr.js.JavaScriptImpl;
import com.fr.stable.ParameterProvider;
import com.fr.stable.StringUtils;
-import javax.swing.*;
+
+import java.awt.BorderLayout;
+import java.awt.Dimension;
+import java.awt.GridBagConstraints;
+import java.awt.GridLayout;
+import javax.swing.BorderFactory;
+import javax.swing.JPanel;
import javax.swing.event.TableModelEvent;
import javax.swing.event.TableModelListener;
-import java.awt.*;
+
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
@@ -28,11 +36,11 @@ import java.util.List;
public class JavaScriptImplPane extends AbstractHyperLinkPane {
private static final int BOTTOM_BORDER = 12;
private UITextField itemNameTextField;
- private JSContentPane jsPane;
+ protected JSContentPane jsPane;
private UITableEditorPane importedJsPane;
private ReportletParameterViewPane parameterPane;
private String[] defaultArgs;
-
+ private boolean modal;
public JavaScriptImplPane() {
this(new String[0]);
@@ -50,8 +58,57 @@ public class JavaScriptImplPane extends AbstractHyperLinkPane {
initComponents();
}
+ public JavaScriptImplPane(String[] args, boolean modal) {
+ this.modal = modal;
+ this.defaultArgs = args;
+ initComponents();
+ }
+
protected void initComponents() {
- parameterPane = new ReportletParameterViewPane(getChartParaType(), getValueEditorPane(), getValueEditorPane());
+ parameterPane = createParameterViewPane();
+ importedJsPane = createImportedJsPane();
+ importedJsPane.setPreferredSize(new Dimension(265, 150));
+
+ jsPane = createJSContentPane(defaultArgs);
+ jsPane.setBorder(BorderFactory.createTitledBorder(new ModLineBorder(ModLineBorder.TOP), com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Report_JavaScript")));
+
+ parameterPane.setPreferredSize(new Dimension(265, 150));
+ JPanel topPane = new JPanel(new GridLayout(1,2));
+ topPane.add(importedJsPane);
+ topPane.add(parameterPane);
+
+ topPane.setBorder(BorderFactory.createEmptyBorder(0, 0, BOTTOM_BORDER, 0));
+
+ this.setLayout(new BorderLayout());
+ this.add(topPane, BorderLayout.NORTH);
+ this.add(jsPane, BorderLayout.CENTER);
+
+ this.reLayoutForChart();
+ }
+
+ protected JSContentPane createJSContentPane(String[] defaultArgs){
+ JSContentPane jsContentPane= new JSContentPane(defaultArgs,modal);
+ jsContentPane.setJsImplUpdateAction(new JSImplUpdateAction() {
+ @Override
+ public void update(JavaScriptImpl javaScript) {
+ if(javaScript != null){
+ updateBean(javaScript);
+ }
+ }
+ });
+ jsContentPane.setJsImplPopulateAction(new JSImplPopulateAction() {
+ @Override
+ public void populate(JavaScriptImpl javaScript) {
+ if(javaScript != null){
+ populateBean(javaScript);
+ }
+ }
+ });
+ return jsContentPane;
+ }
+
+ protected ReportletParameterViewPane createParameterViewPane(){
+ ReportletParameterViewPane parameterPane = new ReportletParameterViewPane(getChartParaType(), getValueEditorPane(), getValueEditorPane());
parameterPane.setBorder(BorderFactory.createTitledBorder(new ModLineBorder(ModLineBorder.TOP), com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Basic_Parameter")));
parameterPane.addTableEditorListener(new TableModelListener() {
public void tableChanged(TableModelEvent e) {
@@ -72,7 +129,10 @@ public class JavaScriptImplPane extends AbstractHyperLinkPane {
parameterChanger(list);
}
});
+ return parameterPane;
+ }
+ protected UITableEditorPane createImportedJsPane(){
OneListTableModel model = new OneListTableModel(com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Report_ReportServerP_Import_JavaScript"), this) {
public UITableEditAction[] createAction() {
@@ -84,26 +144,12 @@ public class JavaScriptImplPane extends AbstractHyperLinkPane {
return new AddJsAction();
}
};
- importedJsPane = new UITableEditorPane(model);
+ UITableEditorPane importedJsPane = new UITableEditorPane(model);
importedJsPane.setBorder(BorderFactory.createTitledBorder(new ModLineBorder(ModLineBorder.TOP), com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Report_ReportServerP_Import_JavaScript")));
- importedJsPane.setPreferredSize(new Dimension(265, 150));
- jsPane = new JSContentPane(defaultArgs);
- jsPane.setBorder(BorderFactory.createTitledBorder(new ModLineBorder(ModLineBorder.TOP), com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Report_JavaScript")));
+ return importedJsPane;
+ }
- parameterPane.setPreferredSize(new Dimension(265, 150));
- JPanel topPane = GUICoreUtils.createBorderLayoutPane(
- importedJsPane, BorderLayout.CENTER,
- parameterPane, BorderLayout.EAST
- );
- topPane.setPreferredSize(new Dimension(300, 150));
- topPane.setBorder(BorderFactory.createEmptyBorder(0, 0, BOTTOM_BORDER, 0));
- this.setLayout(new BorderLayout());
- this.add(topPane, BorderLayout.NORTH);
- this.add(jsPane, BorderLayout.CENTER);
-
- this.reLayoutForChart();
- }
/**
* 参数改变
@@ -140,10 +186,10 @@ public class JavaScriptImplPane extends AbstractHyperLinkPane {
if (javaScriptImpl == null) {
javaScriptImpl = new JavaScriptImpl();
jsPane.reset();
- }else{
+ } else {
jsPane.populate(javaScriptImpl.getContent());
}
-
+ jsPane.updateJSImpl(javaScriptImpl);
int rowCount = javaScriptImpl.getJSImportSize();
String[] value = new String[rowCount];
for (int i = 0; i < rowCount; i++) {
@@ -160,6 +206,7 @@ public class JavaScriptImplPane extends AbstractHyperLinkPane {
public JavaScriptImpl updateBean() {
JavaScriptImpl javaScript = new JavaScriptImpl();
updateBean(javaScript);
+ jsPane.updateJSImpl(javaScript);
return javaScript;
}
diff --git a/designer-base/src/main/java/com/fr/design/javascript/NewJavaScriptImplPane.java b/designer-base/src/main/java/com/fr/design/javascript/NewJavaScriptImplPane.java
new file mode 100644
index 0000000000..23226c6dde
--- /dev/null
+++ b/designer-base/src/main/java/com/fr/design/javascript/NewJavaScriptImplPane.java
@@ -0,0 +1,23 @@
+package com.fr.design.javascript;
+
+
+import com.fr.js.JavaScriptImpl;
+
+
+public class NewJavaScriptImplPane extends JavaScriptImplPane {
+ public NewJavaScriptImplPane(String[] args) {
+ super(args);
+ }
+
+ protected JSContentPane createJSContentPane(String[] defaultArgs){
+ return new JSContentWithDescriptionPane(defaultArgs);
+ }
+
+ public void populate(JavaScriptImpl javaScript) {
+ if (javaScript != null) {
+ populateBean(javaScript);
+ } else {
+ jsPane.reset();
+ }
+ }
+}
diff --git a/designer-base/src/main/java/com/fr/design/javascript/jsapi/CategoryTreeNodesUserObject.java b/designer-base/src/main/java/com/fr/design/javascript/jsapi/CategoryTreeNodesUserObject.java
new file mode 100644
index 0000000000..7b568eff73
--- /dev/null
+++ b/designer-base/src/main/java/com/fr/design/javascript/jsapi/CategoryTreeNodesUserObject.java
@@ -0,0 +1,21 @@
+package com.fr.design.javascript.jsapi;
+
+import com.fr.design.i18n.Toolkit;
+
+public class CategoryTreeNodesUserObject implements JSAPIUserObject {
+ private String value;
+
+ public CategoryTreeNodesUserObject(String value) {
+ this.value = value;
+ }
+
+ @Override
+ public String getValue() {
+ return value;
+ }
+
+ @Override
+ public String getDisplayText() {
+ return Toolkit.i18nText(value);
+ }
+}
diff --git a/designer-base/src/main/java/com/fr/design/javascript/jsapi/JSAPITreeHelper.java b/designer-base/src/main/java/com/fr/design/javascript/jsapi/JSAPITreeHelper.java
new file mode 100644
index 0000000000..a5def05965
--- /dev/null
+++ b/designer-base/src/main/java/com/fr/design/javascript/jsapi/JSAPITreeHelper.java
@@ -0,0 +1,155 @@
+package com.fr.design.javascript.jsapi;
+
+import com.fr.general.IOUtils;
+import com.fr.json.JSONArray;
+import com.fr.json.JSONObject;
+import com.fr.log.FineLoggerFactory;
+import com.fr.stable.StringUtils;
+import java.io.BufferedReader;
+import java.io.InputStreamReader;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import javax.swing.tree.DefaultMutableTreeNode;
+
+public class JSAPITreeHelper {
+ private static final String JSAPI_PATH = "com/fr/design/javascript/jsapi/jsapi.json";
+ private static final String CATEGORY_PATH = "com/fr/design/javascript/jsapi/category.json";
+ private static JSONObject categoryJSON ;
+ private static JSONObject jsapiJSON ;
+
+ static {
+ jsapiJSON = createJSON(JSAPI_PATH);
+ categoryJSON = createJSON(CATEGORY_PATH);
+ }
+
+ private static JSONObject createJSON(String path) {
+ StringBuilder jsonString = new StringBuilder(StringUtils.EMPTY);
+ try (BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(IOUtils.readResource(path)))) {
+ String s;
+ while ((s = bufferedReader.readLine()) != null) {
+ jsonString.append(s);
+ }
+ } catch (Exception e) {
+ FineLoggerFactory.getLogger().error(e.getMessage(), e);
+ }
+ return new JSONObject(jsonString.toString());
+ }
+
+
+ public static void createJSAPITree(DefaultMutableTreeNode rootNode) {
+ createJSAPITree(categoryJSON, rootNode);
+ }
+
+ public static String getDirectCategory(String name) {
+ if (jsapiJSON != null) {
+ Iterator it = jsapiJSON.keys();
+ while (it.hasNext()) {
+ String key = it.next();
+ JSONArray nameArray = jsapiJSON.optJSONArray(key);
+ for (int i = 0; i < nameArray.length(); i++) {
+ if (StringUtils.equals(nameArray.getString(i), name)) {
+ return key;
+ }
+ }
+ }
+ }
+ return null;
+ }
+
+ private static void createJSAPITree(JSONObject jsonObject, DefaultMutableTreeNode rootNode) {
+ if (jsonObject != null && rootNode != null) {
+ Iterator it = jsonObject.keys();
+ while (it.hasNext()) {
+ String key = it.next();
+ JSONObject subNode = jsonObject.optJSONObject(key);
+ if (subNode.size() == 0) {
+ rootNode.add(new DefaultMutableTreeNode(new CategoryTreeNodesUserObject(key)));
+ } else {
+ DefaultMutableTreeNode treeNode = new DefaultMutableTreeNode(new CategoryTreeNodesUserObject(key));
+ rootNode.add(treeNode);
+ createJSAPITree(subNode, treeNode);
+ }
+ }
+ }
+ }
+
+ private static List getAllSubNodes(String name) {
+ return getAllSubNodes(name, categoryJSON);
+ }
+
+ public static List getAllNames() {
+ ArrayList result = new ArrayList<>();
+ if (jsapiJSON != null) {
+ Iterator it = jsapiJSON.keys();
+ while (it.hasNext()) {
+ String key = it.next();
+ JSONArray nameArray = jsapiJSON.optJSONArray(key);
+ for (int i = 0; i < nameArray.length(); i++) {
+ result.add(nameArray.getString(i));
+ }
+ }
+ }
+ return result;
+ }
+
+ public static List getNames(String category) {
+ ArrayList result = new ArrayList<>();
+ List subCategories = getAllSubNodes(category);
+ if (jsapiJSON != null) {
+ for (String subCategory : subCategories) {
+ if (jsapiJSON.containsKey(subCategory)) {
+ JSONArray nameArray = jsapiJSON.optJSONArray(subCategory);
+ for (int i = 0; i < nameArray.length(); i++) {
+ result.add(nameArray.getString(i));
+ }
+ }
+ }
+ }
+ return result;
+ }
+
+ private static List getAllSubNodes(String name, JSONObject jsonObject) {
+ ArrayList result = new ArrayList<>();
+ if (jsonObject != null) {
+ Iterator it = jsonObject.keys();
+ while (it.hasNext()) {
+ String key = it.next();
+ JSONObject subNode = jsonObject.optJSONObject(key);
+ if (subNode.size() == 0) {
+ if (StringUtils.equals(key, name)) {
+ result.add(key);
+ return result;
+ }
+ } else {
+ if (StringUtils.equals(key, name)) {
+ result.add(key);
+ result.addAll(getAllSubNodes(subNode));
+ return result;
+ } else {
+ result.addAll(getAllSubNodes(name, subNode));
+ }
+ }
+ }
+ }
+ return result;
+ }
+
+ private static List getAllSubNodes(JSONObject jsonObject) {
+ ArrayList result = new ArrayList<>();
+ if (jsonObject != null) {
+ Iterator it = jsonObject.keys();
+ while (it.hasNext()) {
+ String key = it.next();
+ JSONObject subNode = jsonObject.optJSONObject(key);
+ if (subNode.size() == 0) {
+ result.add(key);
+ } else {
+ result.add(key);
+ result.addAll(getAllSubNodes(subNode));
+ }
+ }
+ }
+ return result;
+ }
+}
diff --git a/designer-base/src/main/java/com/fr/design/javascript/jsapi/JSAPIUserObject.java b/designer-base/src/main/java/com/fr/design/javascript/jsapi/JSAPIUserObject.java
new file mode 100644
index 0000000000..6790ec71e6
--- /dev/null
+++ b/designer-base/src/main/java/com/fr/design/javascript/jsapi/JSAPIUserObject.java
@@ -0,0 +1,9 @@
+package com.fr.design.javascript.jsapi;
+
+
+public interface JSAPIUserObject {
+
+ String getValue();
+
+ String getDisplayText();
+}
diff --git a/designer-base/src/main/java/com/fr/design/javascript/jsapi/JSImplPopulateAction.java b/designer-base/src/main/java/com/fr/design/javascript/jsapi/JSImplPopulateAction.java
new file mode 100644
index 0000000000..c33d7fe0ee
--- /dev/null
+++ b/designer-base/src/main/java/com/fr/design/javascript/jsapi/JSImplPopulateAction.java
@@ -0,0 +1,7 @@
+package com.fr.design.javascript.jsapi;
+
+import com.fr.js.JavaScriptImpl;
+
+public interface JSImplPopulateAction {
+ void populate(JavaScriptImpl javaScript);
+}
diff --git a/designer-base/src/main/java/com/fr/design/javascript/jsapi/JSImplUpdateAction.java b/designer-base/src/main/java/com/fr/design/javascript/jsapi/JSImplUpdateAction.java
new file mode 100644
index 0000000000..b812d8b875
--- /dev/null
+++ b/designer-base/src/main/java/com/fr/design/javascript/jsapi/JSImplUpdateAction.java
@@ -0,0 +1,7 @@
+package com.fr.design.javascript.jsapi;
+
+import com.fr.js.JavaScriptImpl;
+
+public interface JSImplUpdateAction {
+ void update(JavaScriptImpl javaScript);
+}
diff --git a/designer-base/src/main/java/com/fr/design/lock/LockInfoDialog.java b/designer-base/src/main/java/com/fr/design/lock/LockInfoDialog.java
index 71c9549248..a0fd544d53 100644
--- a/designer-base/src/main/java/com/fr/design/lock/LockInfoDialog.java
+++ b/designer-base/src/main/java/com/fr/design/lock/LockInfoDialog.java
@@ -3,6 +3,7 @@ package com.fr.design.lock;
import com.fr.design.file.TemplateTreePane;
import com.fr.design.gui.ibutton.UIButton;
import com.fr.design.gui.ilable.UILabel;
+import com.fr.design.i18n.DesignSizeI18nManager;
import com.fr.design.i18n.Toolkit;
import com.fr.design.layout.FRGUIPaneFactory;
import com.fr.design.mainframe.DesignerContext;
@@ -44,7 +45,7 @@ public class LockInfoDialog extends JDialog {
panel.add(createControlPane(), BorderLayout.SOUTH);
this.getContentPane().add(panel);
this.setTitle(Toolkit.i18nText("Fine-Design_Basic_Remote_Design_Title_Hint"));
- this.setSize(400, 160);
+ this.setSize(DesignSizeI18nManager.getInstance().i18nDimension("com.fr.design.lock.LockInfoDialog"));
this.setResizable(false);
this.setModal(true);
GUICoreUtils.centerWindow(this);
diff --git a/designer-base/src/main/java/com/fr/design/mainframe/ForbiddenPane.java b/designer-base/src/main/java/com/fr/design/mainframe/ForbiddenPane.java
index f903912115..9c33534c7b 100644
--- a/designer-base/src/main/java/com/fr/design/mainframe/ForbiddenPane.java
+++ b/designer-base/src/main/java/com/fr/design/mainframe/ForbiddenPane.java
@@ -2,6 +2,7 @@ package com.fr.design.mainframe;
import com.fr.design.file.HistoryTemplateListCache;
import com.fr.design.gui.ilable.UILabel;
+import com.fr.design.i18n.DesignSizeI18nManager;
import com.fr.design.i18n.Toolkit;
import com.fr.design.mainframe.guide.base.GuideView;
import com.fr.design.utils.gui.GUICoreUtils;
@@ -105,7 +106,7 @@ public class ForbiddenPane extends JPanel {
super.paintComponent(g2d);
}
};
- refreshButton.setPreferredSize(new Dimension(68, 24));
+ refreshButton.setPreferredSize(DesignSizeI18nManager.getInstance().i18nDimension("com.fr.design.mainframe.ForbiddenPane.refreshButton"));
refreshButton.setForeground(Color.WHITE);
refreshButton.setBorderPainted(false);
refreshButton.setContentAreaFilled(false);
diff --git a/designer-base/src/main/java/com/fr/design/mainframe/theme/edit/cell/CellStyleEditPane.java b/designer-base/src/main/java/com/fr/design/mainframe/theme/edit/cell/CellStyleEditPane.java
index 8efbb1eee5..d8a4096ceb 100644
--- a/designer-base/src/main/java/com/fr/design/mainframe/theme/edit/cell/CellStyleEditPane.java
+++ b/designer-base/src/main/java/com/fr/design/mainframe/theme/edit/cell/CellStyleEditPane.java
@@ -198,7 +198,7 @@ public class CellStyleEditPane extends MultiTabPane {
@Override
protected void layoutContentPane() {
super.layoutContentPane();
- leftcontentPane.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0));
+ leftcontentPane.setBorder(BorderFactory.createMatteBorder(0, 0, 0, 5, original));
}
}
}
diff --git a/designer-base/src/main/java/com/fr/design/report/WatermarkSettingPane.java b/designer-base/src/main/java/com/fr/design/report/WatermarkSettingPane.java
index e79bfcf6b8..f3de6eef96 100644
--- a/designer-base/src/main/java/com/fr/design/report/WatermarkSettingPane.java
+++ b/designer-base/src/main/java/com/fr/design/report/WatermarkSettingPane.java
@@ -34,6 +34,7 @@ public class WatermarkSettingPane extends AbstractTemplateServerSettingPane {
@Override
protected void populateServerSettings() {
WatermarkAttr watermarkAttr = ReportUtils.getWatermarkAttrFromServerConfig();
+ watermarkAttr.setValid(true);
watermarkPane.populate(watermarkAttr);
}
@@ -55,8 +56,9 @@ public class WatermarkSettingPane extends AbstractTemplateServerSettingPane {
public WatermarkAttr update() {
WatermarkAttr watermark = watermarkPane.update();
- if (isUsingServerSettings()) {
- watermark.setValid(false);
+ if (!isUsingServerSettings()) {
+ watermark.setValid(true);
+ watermark.setWaterMarkProvider(WaterMarkProvideConstant.TEMPLATE);
}
return watermark;
}
diff --git a/designer-base/src/main/java/com/fr/design/report/fit/AdaptiveFrmFitAttrModel.java b/designer-base/src/main/java/com/fr/design/report/fit/AdaptiveFrmFitAttrModel.java
index 9e60c6cc84..8c5e8858ac 100644
--- a/designer-base/src/main/java/com/fr/design/report/fit/AdaptiveFrmFitAttrModel.java
+++ b/designer-base/src/main/java/com/fr/design/report/fit/AdaptiveFrmFitAttrModel.java
@@ -13,11 +13,7 @@ import com.fr.report.fit.ReportFitAttr;
public class AdaptiveFrmFitAttrModel extends AbstractFitAttrModelProvider {
public FitType[] getFitTypes() {
- return new FitType[]{
- FitType.DOUBLE_FIT,
- FitType.HORIZONTAL_FIT,
- FitType.NOT_FIT
- };
+ return new FitType[0];
}
public String getFitName() {
diff --git a/designer-base/src/main/java/com/fr/design/report/fit/BaseFitAttrPane.java b/designer-base/src/main/java/com/fr/design/report/fit/BaseFitAttrPane.java
index 2c38d597c4..48d6a5686e 100644
--- a/designer-base/src/main/java/com/fr/design/report/fit/BaseFitAttrPane.java
+++ b/designer-base/src/main/java/com/fr/design/report/fit/BaseFitAttrPane.java
@@ -2,27 +2,17 @@ package com.fr.design.report.fit;
import com.fr.design.ExtraDesignClassManager;
import com.fr.design.beans.BasicBeanPane;
-import com.fr.design.gui.ibutton.UIRadioButton;
import com.fr.design.gui.icombobox.UIComboBox;
import com.fr.design.gui.ilable.UILabel;
-import com.fr.design.i18n.DesignSizeI18nManager;
import com.fr.design.layout.FRGUIPaneFactory;
-import com.fr.design.layout.TableLayoutHelper;
-import com.fr.design.report.fit.menupane.FitPreviewPane;
-import com.fr.design.report.fit.menupane.FitRadioGroup;
-import com.fr.design.report.fit.menupane.FontRadioGroup;
import com.fr.design.report.fit.provider.FitAttrModelProvider;
import com.fr.design.utils.gui.GUICoreUtils;
-import com.fr.general.ComparatorUtils;
import com.fr.report.fit.ReportFitAttr;
import javax.swing.*;
import java.awt.*;
-import java.awt.event.ActionEvent;
-import java.awt.event.ActionListener;
import java.awt.event.ItemListener;
import java.util.ArrayList;
-import java.util.Arrays;
import java.util.Comparator;
import java.util.Set;
import java.util.stream.Collectors;
@@ -35,11 +25,8 @@ public abstract class BaseFitAttrPane extends BasicBeanPane {
protected UILabel belowSetLabel;
protected UIComboBox itemChoose;
protected java.util.List fitAttrModelList = new ArrayList<>();
+ private ReportFitConfigPane fitConfigPane;
- public FontRadioGroup fontRadioGroup;
- public FitRadioGroup adaptRadioGroup;
- public JPanel attrJPanel;
- public FitPreviewPane previewJPanel;
public FitAttrModel fitAttrModel;
private static final int BELOW_SET_COMPONENT_HSPACE = 8;
@@ -64,128 +51,27 @@ public abstract class BaseFitAttrPane extends BasicBeanPane {
protected void populateModel(FitAttrModel fitAttrModel) {
this.fitAttrModel = fitAttrModel;
- if (attrJPanel != null) {
- contentJPanel.remove(attrJPanel);
+ if (fitConfigPane != null) {
+ contentJPanel.remove(fitConfigPane);
}
- if (previewJPanel != null) {
- contentJPanel.remove(previewJPanel);
- }
-
- fontRadioGroup = new FontRadioGroup();
- adaptRadioGroup = new FitRadioGroup();
- initAttrJPanel();
- initPreviewJPanel();
+ this.fitConfigPane = fitAttrModel instanceof CptFitAttrModel ? new ReportFitConfigPane(fitAttrModel, true) : new FormFitConfigPane(fitAttrModel, true);
+ contentJPanel.add(fitConfigPane);
}
-
- protected void initAttrJPanel() {
- int colCount = fitAttrModel.getFitTypes().length + 1;
- Component[][] components = new Component[2][colCount];
- initFitRadioGroup(fontRadioGroup, i18nText("Fine-Designer_Fit-Font"), new String[]{i18nText("Fine-Designer_Fit"), i18nText("Fine-Designer_Fit-No")}, components[0]);
- initFitRadioGroup(adaptRadioGroup, fitAttrModel.getFitName(), Arrays.stream(fitAttrModel.getFitTypes()).map(FitType::description).toArray(String[]::new), components[1]);
-
- double[] rowSize = new double[2];
- double[] columnSize = new double[colCount];
- for (int i = 0; i < rowSize.length; i++) {
- rowSize[i] = 20;
- }
- for (int i = 0; i < columnSize.length; i++) {
- if (i == 0) {
- columnSize[i] = DesignSizeI18nManager.getInstance().i18nDimension("com.fr.design.report.fit.firstColumn").getWidth();
- } else {
- columnSize[i] = DesignSizeI18nManager.getInstance().i18nDimension("com.fr.design.report.fit.column").getWidth();
- }
- }
-
- attrJPanel = TableLayoutHelper.createTableLayoutPane(components, rowSize, columnSize);
- attrJPanel.setBorder(BorderFactory.createEmptyBorder(5, 0, 10, 0));
- contentJPanel.add(attrJPanel);
- }
-
- private void initFitRadioGroup(FitRadioGroup fitRadioGroup, String name, String[] options, Component[] components) {
- components[0] = new UILabel(name);
- for (int i = 0; i < options.length; i++) {
-
- if (options[i] != null) {
- UIRadioButton fontFitRadio = new UIRadioButton(options[i]);
- fitRadioGroup.add(fontFitRadio);
- components[i + 1] = fontFitRadio;
- } else {
- components[i + 1] = null;
- }
- }
- fitRadioGroup.addActionListener(getPreviewActionListener());
- }
-
- protected ActionListener getPreviewActionListener() {
- return new ActionListener() {
- @Override
- public void actionPerformed(ActionEvent e) {
- refreshPreviewJPanel();
- }
- };
- }
-
- protected void refreshPreviewJPanel() {
- String previewIndex = getPreviewIndex();
- previewJPanel.refreshPreview(previewIndex, fontRadioGroup.isEnabled());
- }
-
- protected String getPreviewIndex() {
- return getStateInPC(adaptRadioGroup.getSelectRadioIndex()) + "" + fontRadioGroup.getSelectRadioIndex();
- }
-
- protected void initPreviewJPanel() {
- previewJPanel = new FitPreviewPane();
- previewJPanel.setBorder(BorderFactory.createEmptyBorder(0, getPreviewJPanelLeft(), 0, 0));
- contentJPanel.add(previewJPanel);
- }
-
- private int getPreviewJPanelLeft() {
- int left = 0;
- if (belowSetLabel.getPreferredSize() != null) {
- left = belowSetLabel.getPreferredSize().width + BELOW_SET_COMPONENT_HSPACE;
- }
- return left;
- }
-
- protected int getStateInPC(int index) {
- FitType[] fitTypes = fitAttrModel.getFitTypes();
- return fitTypes[index].getState();
- }
-
- protected int getOptionIndex(int state) {
- FitType[] fitTypes = fitAttrModel.getFitTypes();
- for (int i = 0; i < fitTypes.length; i++) {
- if (ComparatorUtils.equals(state, fitTypes[i].getState())) {
- return i;
- }
- }
- return 0;
- }
-
-
@Override
public void populateBean(ReportFitAttr ob) {
- fontRadioGroup.selectIndexButton(ob.isFitFont() ? 0 : 1);
- adaptRadioGroup.selectIndexButton(getOptionIndex(ob.fitStateInPC()));
- refreshPreviewJPanel();
+ this.fitConfigPane.populateBean(ob);
}
@Override
public ReportFitAttr updateBean() {
- ReportFitAttr reportFitAttr = new ReportFitAttr();
- reportFitAttr.setFitFont(fontRadioGroup.isFontFit());
- reportFitAttr.setFitStateInPC(getStateInPC(adaptRadioGroup.getSelectRadioIndex()));
- return reportFitAttr;
+ return this.fitConfigPane.updateBean();
}
public void setEnabled(boolean enabled) {
super.setEnabled(enabled);
- fontRadioGroup.setEnabled(enabled);
- adaptRadioGroup.setEnabled(enabled);
- refreshPreviewJPanel();
+ this.fitConfigPane.setEnabled(enabled);
}
@Override
diff --git a/designer-base/src/main/java/com/fr/design/report/fit/FitAttrModel.java b/designer-base/src/main/java/com/fr/design/report/fit/FitAttrModel.java
index 1d1ac02f0d..2a834eebd8 100644
--- a/designer-base/src/main/java/com/fr/design/report/fit/FitAttrModel.java
+++ b/designer-base/src/main/java/com/fr/design/report/fit/FitAttrModel.java
@@ -3,6 +3,8 @@ package com.fr.design.report.fit;
import com.fr.design.mainframe.JTemplate;
import com.fr.report.fit.ReportFitAttr;
+import java.util.Arrays;
+
public interface FitAttrModel {
/**
* @Description 名称,比如:普通报表、决策报表等
@@ -19,6 +21,12 @@ public interface FitAttrModel {
**/
FitType[] getFitTypes();
+ /**
+ * @Description 表格自适应选项名称集合
+ **/
+ default String[] getFitTypeNames(){
+ return Arrays.stream(getFitTypes()).map(FitType::description).toArray(String[]::new);
+ }
/**
* @Description 获取全局的自适应属性
@@ -41,4 +49,5 @@ public interface FitAttrModel {
* @param: jTemplate
**/
boolean isAvailable(JTemplate jTemplate);
+
}
diff --git a/designer-base/src/main/java/com/fr/design/report/fit/FitType.java b/designer-base/src/main/java/com/fr/design/report/fit/FitType.java
index 00aceb6eb1..dd4888b4e5 100644
--- a/designer-base/src/main/java/com/fr/design/report/fit/FitType.java
+++ b/designer-base/src/main/java/com/fr/design/report/fit/FitType.java
@@ -60,6 +60,17 @@ public enum FitType {
return DEFAULT;
}
+ public static FitType parseByFitState(int state) {
+
+ for (FitType attrState : values()) {
+ if (attrState.state == state) {
+ return attrState;
+ }
+ }
+
+ return DEFAULT;
+ }
+
public int getState() {
return this.state;
}
diff --git a/designer-base/src/main/java/com/fr/design/report/fit/FormFitAttrModelType.java b/designer-base/src/main/java/com/fr/design/report/fit/FormFitAttrModelType.java
new file mode 100644
index 0000000000..a77566442d
--- /dev/null
+++ b/designer-base/src/main/java/com/fr/design/report/fit/FormFitAttrModelType.java
@@ -0,0 +1,128 @@
+package com.fr.design.report.fit;
+
+
+import com.fr.design.designer.properties.items.Item;
+import com.fr.form.fit.common.LightTool;
+import com.fr.form.main.BodyScaleAttrTransformer;
+import com.fr.form.main.Form;
+import com.fr.form.ui.container.WAbsoluteLayout;
+import com.fr.form.ui.container.WBodyLayoutType;
+import com.fr.form.ui.container.WFitLayout;
+
+public enum FormFitAttrModelType {
+ PLAIN_FORM_FIT_ATTR_MODEL {
+ @Override
+ public FitAttrModel getFitAttrModel() {
+ return new FrmFitAttrModel();
+ }
+
+ @Override
+ public Item[] getFitLayoutScaleAttr() {
+ return new Item[]{
+ new Item(com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Form_Attr_Bidirectional_Adaptive"), WFitLayout.STATE_FULL),
+ new Item(com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Form_Attr_Horizontal_Adaptive"), WFitLayout.STATE_ORIGIN)};
+
+ }
+
+ @Override
+ public Item[] getAbsoluteLayoutSaleAttr() {
+ return new Item[]{
+ new Item(com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Form_Widget_Scaling_Mode_Fit"), WAbsoluteLayout.STATE_FIT),
+ new Item(com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Form_Widget_Scaling_Mode_Fixed"), WAbsoluteLayout.STATE_FIXED)
+ };
+ }
+
+
+ @Override
+ public int getScaleAttrShowIndex(WFitLayout wFitLayout) {
+ int scale = wFitLayout.getScaleAttr();
+ if (wFitLayout.getBodyLayoutType() == WBodyLayoutType.FIT) {
+ return BodyScaleAttrTransformer.getFitBodyCompStateFromScaleAttr(scale);
+ } else {
+ return BodyScaleAttrTransformer.getAbsoluteBodyCompStateFromScaleAttr(scale);
+ }
+ }
+
+ @Override
+ public int parseScaleAttrFromShowIndex(int showIndex, WBodyLayoutType wBodyLayoutType) {
+ if (wBodyLayoutType == WBodyLayoutType.FIT) {
+ if (showIndex == 0) {
+ return WFitLayout.SCALE_FULL;
+ } else {
+ return WFitLayout.SCALE_HOR;
+ }
+ } else {
+ if (showIndex == 0) {
+ return WFitLayout.SCALE_FULL;
+ } else {
+ return WFitLayout.SCALE_NO;
+ }
+ }
+ }
+
+
+ },
+ NEW_FORM_FIT_ATTR_MODEL {
+ @Override
+ public FitAttrModel getFitAttrModel() {
+ return new AdaptiveFrmFitAttrModel();
+ }
+
+ @Override
+ public Item[] getFitLayoutScaleAttr() {
+ return new Item[]{
+ new Item(com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Form_Attr_Bidirectional_Adaptive"), WFitLayout.STATE_FULL),
+ new Item(com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Form_Attr_Horizontal_Adaptive"), WFitLayout.STATE_ORIGIN),
+ new Item(com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Form_Widget_Scaling_Mode_Fixed"), 2)};
+ }
+
+ @Override
+ public Item[] getAbsoluteLayoutSaleAttr() {
+ return new Item[]{
+ new Item(com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Form_Attr_Bidirectional_Adaptive"), WFitLayout.STATE_FULL),
+ new Item(com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Form_Attr_Horizontal_Adaptive"), WFitLayout.STATE_ORIGIN),
+ new Item(com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Form_Widget_Scaling_Mode_Fixed"), 2)};
+ }
+
+
+ @Override
+ public int getScaleAttrShowIndex(WFitLayout wFitLayout) {
+ int scale = wFitLayout.getScaleAttr();
+ if (scale == WFitLayout.SCALE_NO) {
+ return 2;
+ } else if (scale == WFitLayout.SCALE_HOR) {
+ return 1;
+ } else {
+ return 0;
+ }
+ }
+
+ @Override
+ public int parseScaleAttrFromShowIndex(int showIndex, WBodyLayoutType wBodyLayoutType) {
+ if (showIndex == 0) {
+ return WFitLayout.SCALE_FULL;
+ } else if (showIndex == 1) {
+ return WFitLayout.SCALE_HOR;
+ } else {
+ return WFitLayout.SCALE_NO;
+ }
+ }
+
+
+ };
+
+ public abstract FitAttrModel getFitAttrModel();
+
+ public abstract Item[] getFitLayoutScaleAttr();
+
+ public abstract Item[] getAbsoluteLayoutSaleAttr();
+
+ public abstract int getScaleAttrShowIndex(WFitLayout wFitLayout);
+
+ public abstract int parseScaleAttrFromShowIndex(int showIndex, WBodyLayoutType wBodyLayoutType);
+
+
+ public static FormFitAttrModelType parse(Form form) {
+ return LightTool.containNewFormFlag(form) ? NEW_FORM_FIT_ATTR_MODEL : PLAIN_FORM_FIT_ATTR_MODEL;
+ }
+}
diff --git a/designer-base/src/main/java/com/fr/design/report/fit/FormFitConfigPane.java b/designer-base/src/main/java/com/fr/design/report/fit/FormFitConfigPane.java
new file mode 100644
index 0000000000..08b70cb772
--- /dev/null
+++ b/designer-base/src/main/java/com/fr/design/report/fit/FormFitConfigPane.java
@@ -0,0 +1,59 @@
+package com.fr.design.report.fit;
+
+import com.fr.design.gui.ilable.UILabel;
+import com.fr.design.i18n.Toolkit;
+import com.fr.design.layout.FRGUIPaneFactory;
+import com.fr.report.fit.ReportFitAttr;
+
+import javax.swing.JPanel;
+import java.awt.BorderLayout;
+import java.awt.Color;
+import java.awt.Component;
+
+public class FormFitConfigPane extends ReportFitConfigPane {
+ private static final int DEFAULT_ITEM = 0;
+ private static final int CUSTOM_ITEM = 1;
+
+ public FormFitConfigPane(FitAttrModel fitAttrModel) {
+ this(fitAttrModel, false);
+ }
+
+ public FormFitConfigPane(FitAttrModel fitAttrModel, boolean globalConfig) {
+ super(fitAttrModel, globalConfig);
+ }
+
+ protected JPanel initECConfigPane() {
+ JPanel jPanel = FRGUIPaneFactory.createBorderLayout_S_Pane();
+ if (fitAttrModel.getFitTypeNames().length != 0) {
+ Component[] ecComponents = new Component[fitAttrModel.getFitTypeNames().length + 1];
+ initRadioGroup(ecConfigRadioGroup, fitAttrModel.getFitName(), fitAttrModel.getFitTypeNames(), ecComponents);
+ jPanel.add(createSubAttrPane(ecComponents), BorderLayout.CENTER);
+ jPanel.add(createTipPane(), BorderLayout.SOUTH);
+ }
+ return jPanel;
+ }
+
+ private JPanel createTipPane() {
+ JPanel jPanel = FRGUIPaneFactory.createVerticalFlowLayout_S_Pane(true);
+ UILabel label1 = new UILabel(Toolkit.i18nText("Fine-Design_Form_PC_FIT_Config_Tip1"));
+ jPanel.add(label1);
+ label1.setForeground(Color.lightGray);
+ UILabel label2 = new UILabel(Toolkit.i18nText("Fine-Design_Form_PC_FIT_Config_Tip2"));
+ jPanel.add(label2);
+ label2.setForeground(Color.lightGray);
+ return jPanel;
+ }
+
+ protected void refreshPreviewJPanel() {
+ previewJPanel.refreshPreview(fontRadioGroup.isFontFit());
+ }
+
+ protected void populateECConfigRadioGroup(int fitStateInPC) {
+ ecConfigRadioGroup.selectIndexButton(fitStateInPC == 0 ? DEFAULT_ITEM : CUSTOM_ITEM);
+ }
+
+ protected void updateECConfigRadioGroup(ReportFitAttr reportFitAttr) {
+ reportFitAttr.setFitStateInPC(ecConfigRadioGroup.getSelectRadioIndex());
+ }
+
+}
diff --git a/designer-base/src/main/java/com/fr/design/report/fit/FrmFitAttrModel.java b/designer-base/src/main/java/com/fr/design/report/fit/FrmFitAttrModel.java
index c4bdaf6de2..81ee474de1 100644
--- a/designer-base/src/main/java/com/fr/design/report/fit/FrmFitAttrModel.java
+++ b/designer-base/src/main/java/com/fr/design/report/fit/FrmFitAttrModel.java
@@ -17,7 +17,7 @@ public class FrmFitAttrModel implements FitAttrModel {
@Override
public String getFitName() {
- return Toolkit.i18nText("Fine-Designer_Fit-Element");
+ return Toolkit.i18nText("Fine-Design_Form_PC_FIT_Config_EC_Label");
}
public FitType[] getFitTypes() {
@@ -29,6 +29,13 @@ public class FrmFitAttrModel implements FitAttrModel {
};
}
+ @Override
+ public String[] getFitTypeNames() {
+ return new String[]{
+ Toolkit.i18nText("Fine-Designer_Fit-Default"), Toolkit.i18nText("Fine-Design_Basic_Custom")
+ };
+ }
+
@Override
public ReportFitAttr getGlobalReportFitAttr() {
return ReportFitConfig.getInstance().getFrmFitAttr();
diff --git a/designer-base/src/main/java/com/fr/design/report/fit/NewFitPreviewPane.java b/designer-base/src/main/java/com/fr/design/report/fit/NewFitPreviewPane.java
new file mode 100644
index 0000000000..8b0710de3f
--- /dev/null
+++ b/designer-base/src/main/java/com/fr/design/report/fit/NewFitPreviewPane.java
@@ -0,0 +1,71 @@
+package com.fr.design.report.fit;
+
+import com.fr.base.GraphHelper;
+import com.fr.general.FRFont;
+
+import javax.swing.JPanel;
+import java.awt.Color;
+import java.awt.Dimension;
+import java.awt.Font;
+import java.awt.FontMetrics;
+import java.awt.Graphics;
+
+
+public class NewFitPreviewPane extends JPanel {
+ private boolean fitFont = false;
+ private FitType fitType = FitType.DOUBLE_FIT;
+ private static final Color DEFAULT_PAINT_COLOR = Color.decode("#419BF9");
+ private static final int FIT_FONT_SIZE = 15;
+ private static final int NO_FIT_FONT_SIZE = 9;
+ private static final Dimension NO_FIT_CONTAINER_DIMENSION = new Dimension(200, 136);
+
+ @Override
+ public void paint(Graphics g) {
+ super.paint(g);
+ g.setColor(Color.GRAY);
+ GraphHelper.drawRect(g, 1, 1, this.getWidth() - 2, this.getHeight() - 2);
+ g.setColor(DEFAULT_PAINT_COLOR);
+ FRFont textFont = FRFont.getInstance(FRFont.DEFAULT_FONTNAME, Font.PLAIN, fitFont ? FIT_FONT_SIZE : NO_FIT_FONT_SIZE);
+ g.setFont(textFont);
+ Dimension dimension = calculateCellDimension();
+ GraphHelper.drawLine(g, 1, dimension.height, dimension.width * 2 - 1, dimension.height);
+ GraphHelper.drawLine(g, dimension.width, 1, dimension.width, dimension.height * 2 - 1);
+ GraphHelper.drawRect(g, 1, 1, dimension.width * 2 - 2, dimension.height * 2 - 2);
+ double startX = calculateTextDrawStartX(dimension.width, this.getFontMetrics(textFont), "text1");
+ double startY = calculateTextDrawStartY(dimension.height);
+ GraphHelper.drawString(g, "text1", startX, startY);
+ GraphHelper.drawString(g, "text2", dimension.width + startX, startY);
+ GraphHelper.drawString(g, "text3", startX, dimension.height + startY);
+ GraphHelper.drawString(g, "text4", dimension.width + startX, dimension.height + startY);
+ }
+
+ private Dimension calculateCellDimension() {
+ if (fitType == FitType.DOUBLE_FIT) {
+ return new Dimension(this.getWidth() / 2, this.getHeight() / 2);
+ } else if (fitType == FitType.NOT_FIT) {
+ return new Dimension(NO_FIT_CONTAINER_DIMENSION.width / 2, NO_FIT_CONTAINER_DIMENSION.height / 2);
+ } else {
+ return new Dimension(this.getWidth() / 2, NO_FIT_CONTAINER_DIMENSION.height / 2);
+ }
+ }
+
+ private double calculateTextDrawStartX(int containerWidth, FontMetrics fontMetrics, String text) {
+ return (containerWidth - fontMetrics.stringWidth(text)) / 2.0D;
+ }
+
+ private double calculateTextDrawStartY(int containerHeight) {
+ return containerHeight / 2.0D;
+ }
+
+ public void refreshPreview(boolean fitFont, FitType fitType) {
+ this.fitFont = fitFont;
+ this.fitType = fitType;
+ repaint();
+ }
+
+ public void refreshPreview(boolean fitFont) {
+ this.fitFont = fitFont;
+ repaint();
+ }
+
+}
diff --git a/designer-base/src/main/java/com/fr/design/report/fit/ReportFitConfigPane.java b/designer-base/src/main/java/com/fr/design/report/fit/ReportFitConfigPane.java
new file mode 100644
index 0000000000..399e7fef93
--- /dev/null
+++ b/designer-base/src/main/java/com/fr/design/report/fit/ReportFitConfigPane.java
@@ -0,0 +1,172 @@
+package com.fr.design.report.fit;
+
+import com.fr.design.gui.ibutton.UIRadioButton;
+import com.fr.design.gui.ilable.UILabel;
+import com.fr.design.i18n.DesignSizeI18nManager;
+import com.fr.design.layout.FRGUIPaneFactory;
+import com.fr.design.layout.TableLayoutHelper;
+import com.fr.design.report.fit.menupane.FitRadioGroup;
+import com.fr.design.report.fit.menupane.FontRadioGroup;
+import com.fr.general.ComparatorUtils;
+import com.fr.report.fit.ReportFitAttr;
+
+import javax.swing.BorderFactory;
+import javax.swing.JPanel;
+import java.awt.BorderLayout;
+import java.awt.Component;
+import java.awt.Dimension;
+import java.awt.FlowLayout;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+
+import static com.fr.design.i18n.Toolkit.i18nText;
+
+public class ReportFitConfigPane extends JPanel {
+ public FontRadioGroup fontRadioGroup;
+ public FitRadioGroup ecConfigRadioGroup;
+ protected NewFitPreviewPane previewJPanel;
+ protected FitAttrModel fitAttrModel;
+ protected boolean globalConfig;
+
+
+ public ReportFitConfigPane(FitAttrModel fitAttrModel, boolean globalConfig) {
+ this.fitAttrModel = fitAttrModel;
+ this.globalConfig = globalConfig;
+ initComponent();
+ }
+
+ private void initComponent() {
+ JPanel contentJPanel = FRGUIPaneFactory.createVerticalFlowLayout_Pane(false, FlowLayout.LEFT, 0, 0);
+ this.add(contentJPanel);
+ fontRadioGroup = new FontRadioGroup();
+ ecConfigRadioGroup = new FitRadioGroup();
+ contentJPanel.add(initAttrJPanel());
+ contentJPanel.add(initPreviewJPanel());
+ }
+
+ private JPanel initAttrJPanel() {
+ JPanel jPanel = FRGUIPaneFactory.createBorderLayout_S_Pane();
+ Component[] fontComponents = new Component[3];
+ initRadioGroup(fontRadioGroup, i18nText("Fine-Designer_Fit-Font"), new String[]{i18nText("Fine-Designer_Fit"), i18nText("Fine-Designer_Fit-No")}, fontComponents);
+ jPanel.add(createSubAttrPane(fontComponents), BorderLayout.NORTH);
+ jPanel.add(initECConfigPane(), BorderLayout.CENTER);
+ return jPanel;
+ }
+
+ protected JPanel initECConfigPane() {
+ JPanel jPanel = FRGUIPaneFactory.createBorderLayout_S_Pane();
+ Component[] ecComponents = new Component[fitAttrModel.getFitTypeNames().length + 1];
+ initRadioGroup(ecConfigRadioGroup, fitAttrModel.getFitName(), fitAttrModel.getFitTypeNames(), ecComponents);
+ jPanel.add(createSubAttrPane(ecComponents), BorderLayout.CENTER);
+ return jPanel;
+ }
+
+
+ protected JPanel createSubAttrPane(Component[] components) {
+ double[] rowSize = new double[]{20};
+ double[] columnSize = new double[components.length];
+ for (int i = 0; i < columnSize.length; i++) {
+ if (i == 0) {
+ columnSize[i] = DesignSizeI18nManager.getInstance().i18nDimension("com.fr.design.report.fit.firstColumn").getWidth();
+ } else {
+ columnSize[i] = DesignSizeI18nManager.getInstance().i18nDimension("com.fr.design.report.fit.column").getWidth();
+ }
+ }
+
+ JPanel attrJPanel = TableLayoutHelper.createTableLayoutPane(new Component[][]{components}, rowSize, columnSize);
+ attrJPanel.setBorder(BorderFactory.createEmptyBorder(5, 0, 10, 0));
+ return attrJPanel;
+ }
+
+ protected void initRadioGroup(FitRadioGroup fitRadioGroup, String name, String[] options, Component[] components) {
+ components[0] = new UILabel(name);
+ for (int i = 0; i < options.length; i++) {
+
+ if (options[i] != null) {
+ UIRadioButton fontFitRadio = new UIRadioButton(options[i]);
+ fitRadioGroup.add(fontFitRadio);
+ components[i + 1] = fontFitRadio;
+ } else {
+ components[i + 1] = null;
+ }
+ }
+ fitRadioGroup.addActionListener(getPreviewActionListener());
+ }
+
+ private ActionListener getPreviewActionListener() {
+ return new ActionListener() {
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ refreshPreviewJPanel();
+ }
+ };
+ }
+
+ public void refreshPreviewJPanel(FitType fitType) {
+ previewJPanel.refreshPreview(fontRadioGroup.isFontFit(), fitType);
+ }
+
+ protected void refreshPreviewJPanel() {
+ previewJPanel.refreshPreview(fontRadioGroup.isFontFit(), FitType.parse(updateBean()));
+ }
+
+ private JPanel initPreviewJPanel() {
+ JPanel wrapperPane = FRGUIPaneFactory.createBorderLayout_S_Pane();
+ previewJPanel = new NewFitPreviewPane();
+ wrapperPane.add(previewJPanel, BorderLayout.CENTER);
+ int leftIndent = globalConfig ? (int) DesignSizeI18nManager.getInstance().i18nDimension("com.fr.design.report.fit.firstColumn").getWidth() : 0;
+ wrapperPane.setBorder(BorderFactory.createEmptyBorder(0, leftIndent, 0, 0));
+ wrapperPane.setPreferredSize(new Dimension(300 + leftIndent, 204));
+ return wrapperPane;
+ }
+
+
+ public void populateBean(ReportFitAttr ob) {
+ fontRadioGroup.selectIndexButton(ob.isFitFont() ? 0 : 1);
+ populateECConfigRadioGroup(ob.fitStateInPC());
+ refreshPreviewJPanel();
+ }
+
+ protected void populateECConfigRadioGroup(int fitStateInPC){
+ ecConfigRadioGroup.selectIndexButton(getOptionIndex(fitStateInPC));
+ }
+
+
+ protected void updateECConfigRadioGroup(ReportFitAttr reportFitAttr){
+ reportFitAttr.setFitStateInPC(getStateInPC(ecConfigRadioGroup.getSelectRadioIndex()));
+ }
+
+ public ReportFitAttr updateBean() {
+ ReportFitAttr reportFitAttr = new ReportFitAttr();
+ reportFitAttr.setFitFont(fontRadioGroup.isFontFit());
+ updateECConfigRadioGroup(reportFitAttr);
+ return reportFitAttr;
+ }
+
+
+ protected int getStateInPC(int index) {
+ FitType[] fitTypes = fitAttrModel.getFitTypes();
+ if (index > fitTypes.length - 1) {
+ return index;
+ }
+ return fitTypes[index].getState();
+ }
+
+ protected int getOptionIndex(int state) {
+ FitType[] fitTypes = fitAttrModel.getFitTypes();
+ for (int i = 0; i < fitTypes.length; i++) {
+ if (ComparatorUtils.equals(state, fitTypes[i].getState())) {
+ return i;
+ }
+ }
+ return 0;
+ }
+
+
+ public void setEnabled(boolean enabled) {
+ super.setEnabled(enabled);
+ fontRadioGroup.setEnabled(enabled);
+ ecConfigRadioGroup.setEnabled(enabled);
+ }
+
+}
diff --git a/designer-base/src/main/java/com/fr/design/web/CustomIconPane.java b/designer-base/src/main/java/com/fr/design/web/CustomIconPane.java
index 9e03dcc9dc..564b09d467 100644
--- a/designer-base/src/main/java/com/fr/design/web/CustomIconPane.java
+++ b/designer-base/src/main/java/com/fr/design/web/CustomIconPane.java
@@ -4,14 +4,14 @@ import com.fr.base.BaseUtils;
import com.fr.base.GraphHelper;
import com.fr.base.Icon;
import com.fr.base.IconManager;
-import com.fr.design.designer.IntervalConstants;
import com.fr.design.dialog.BasicPane;
import com.fr.design.dialog.DialogActionAdapter;
import com.fr.design.dialog.FineJOptionPane;
import com.fr.design.gui.ibutton.UIButton;
import com.fr.design.gui.icontainer.UIScrollPane;
+import com.fr.design.gui.ifilechooser.FileChooserArgs;
+import com.fr.design.gui.ifilechooser.FileChooserFactory;
import com.fr.design.gui.ifilechooser.FileChooserProvider;
-import com.fr.design.gui.ifilechooser.JavaFxNativeFileChooser;
import com.fr.design.gui.ilable.UILabel;
import com.fr.design.gui.iscrollbar.UIScrollBar;
import com.fr.design.gui.itextarea.DescriptionTextArea;
@@ -30,23 +30,35 @@ import com.fr.stable.ListMap;
import com.fr.stable.StringUtils;
import com.fr.transaction.Configurations;
import com.fr.transaction.WorkerFacade;
-import javafx.stage.FileChooser;
-import javax.swing.*;
+import java.awt.BorderLayout;
+import java.awt.Color;
+import java.awt.Component;
+import java.awt.Cursor;
+import java.awt.Dimension;
+import java.awt.FlowLayout;
+import java.awt.Graphics;
+import java.awt.Graphics2D;
+import java.awt.Image;
+import java.awt.Window;
+import javax.swing.ButtonGroup;
+import javax.swing.ImageIcon;
+import javax.swing.JComponent;
+import javax.swing.JFileChooser;
+import javax.swing.JOptionPane;
+import javax.swing.JPanel;
+import javax.swing.JToggleButton;
+import javax.swing.ScrollPaneConstants;
+import javax.swing.SwingUtilities;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
-import javax.swing.filechooser.FileNameExtensionFilter;
import javax.swing.plaf.basic.BasicButtonUI;
-import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
-import java.io.File;
-import java.io.FileOutputStream;
-import java.io.IOException;
/**
* carl:自定义Icon编辑
@@ -414,7 +426,7 @@ public class CustomIconPane extends BasicPane {
browseButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
- onBrowseButtonClicked();
+ onBrowseButtonClicked(SwingUtilities.getWindowAncestor(EditIconDialog.this));
}
});
@@ -449,12 +461,11 @@ public class CustomIconPane extends BasicPane {
this.add(centerPane, BorderLayout.CENTER);
}
- private void onBrowseButtonClicked() {
+ private void onBrowseButtonClicked(Window parent) {
// carl:不知道是否只要png格式,反正导出时全部都转成png了
- FileChooserProvider fileChooserProvider = new JavaFxNativeFileChooser.Builder().
- filter("Icon Image File", "*.jpg", "*.jpeg", "*.png", "*.gif").
- build();
- if (JFileChooser.APPROVE_OPTION == fileChooserProvider.showDialog(DesignerContext.getDesignerFrame())) {
+ FileChooserProvider fileChooserProvider = FileChooserFactory.createFileChooser(FileChooserArgs.newBuilder().
+ setFilter("Icon Image File", "*.jpg", "*.jpeg", "*.png", "*.gif").build());
+ if (JFileChooser.APPROVE_OPTION == fileChooserProvider.showDialog(parent)) {
String path = fileChooserProvider.getSelectedFile().getAbsolutePath();
// 图片存储有最大值48*48限制,没有超过最大值时,按原图大小存储,超过最大值后,压缩至最大值存储
Image image = BaseUtils.readImage(path);
diff --git a/designer-base/src/main/java/com/fr/design/worker/save/CallbackSaveWorker.java b/designer-base/src/main/java/com/fr/design/worker/save/CallbackSaveWorker.java
index f8a0f5d4fa..ed2597f1d7 100644
--- a/designer-base/src/main/java/com/fr/design/worker/save/CallbackSaveWorker.java
+++ b/designer-base/src/main/java/com/fr/design/worker/save/CallbackSaveWorker.java
@@ -63,6 +63,15 @@ public class CallbackSaveWorker extends SaveWorker {
}
}
+ public void addSuccessCallbackBeforeLast(Runnable successRunnable) {
+ if (successRunnableList == null) {
+ successRunnableList = new LinkedList<>();
+ }
+ if (successRunnable != null) {
+ successRunnableList.add(successRunnableList.size() - 1, successRunnable);
+ }
+ }
+
public void addFailCallback(Runnable failRunnable) {
if (failRunnableList == null) {
failRunnableList = new LinkedList<>();
diff --git a/designer-base/src/main/resources/com/fr/design/i18n/dimension_en.properties b/designer-base/src/main/resources/com/fr/design/i18n/dimension_en.properties
index 786fe2ad0c..aa4a83f3e4 100644
--- a/designer-base/src/main/resources/com/fr/design/i18n/dimension_en.properties
+++ b/designer-base/src/main/resources/com/fr/design/i18n/dimension_en.properties
@@ -12,4 +12,6 @@ com.fr.design.web.pane.text.field=450*20
com.fr.design.actions.server.dialog=800*630
com.fr.design.report.fit.templatePane.dialog=800*400
com.fr.design.report.fit.firstColumn=120*20
-com.fr.design.report.fit.column=160*20
\ No newline at end of file
+com.fr.design.report.fit.column=160*20
+com.fr.design.lock.LockInfoDialog=500*180
+com.fr.design.mainframe.ForbiddenPane.refreshButton=75*24
\ No newline at end of file
diff --git a/designer-base/src/main/resources/com/fr/design/i18n/dimension_ja_JP.properties b/designer-base/src/main/resources/com/fr/design/i18n/dimension_ja_JP.properties
index 0956fa79d7..96558262fd 100644
--- a/designer-base/src/main/resources/com/fr/design/i18n/dimension_ja_JP.properties
+++ b/designer-base/src/main/resources/com/fr/design/i18n/dimension_ja_JP.properties
@@ -11,4 +11,6 @@ com.fr.design.web.pane.text.field=400*20
com.fr.design.actions.server.dialog=700*630
com.fr.design.report.fit.templatePane.dialog=600*400
com.fr.design.report.fit.firstColumn=170*20
-com.fr.design.report.fit.column=100*20
\ No newline at end of file
+com.fr.design.report.fit.column=100*20
+com.fr.design.lock.LockInfoDialog=500*180
+com.fr.design.mainframe.ForbiddenPane.refreshButton=68*24
\ No newline at end of file
diff --git a/designer-base/src/main/resources/com/fr/design/i18n/dimension_ko_KR.properties b/designer-base/src/main/resources/com/fr/design/i18n/dimension_ko_KR.properties
index 17031793ff..05e20c7aa8 100644
--- a/designer-base/src/main/resources/com/fr/design/i18n/dimension_ko_KR.properties
+++ b/designer-base/src/main/resources/com/fr/design/i18n/dimension_ko_KR.properties
@@ -11,4 +11,6 @@ com.fr.design.web.pane.text.field=450*20
com.fr.design.actions.server.dialog=700*630
com.fr.design.report.fit.templatePane.dialog=600*400
com.fr.design.report.fit.firstColumn=130*20
-com.fr.design.report.fit.column=100*20
\ No newline at end of file
+com.fr.design.report.fit.column=100*20
+com.fr.design.lock.LockInfoDialog=500*180
+com.fr.design.mainframe.ForbiddenPane.refreshButton=80*24
\ No newline at end of file
diff --git a/designer-base/src/main/resources/com/fr/design/i18n/dimension_zh.properties b/designer-base/src/main/resources/com/fr/design/i18n/dimension_zh.properties
index 931b79b44f..a4b2993e60 100644
--- a/designer-base/src/main/resources/com/fr/design/i18n/dimension_zh.properties
+++ b/designer-base/src/main/resources/com/fr/design/i18n/dimension_zh.properties
@@ -12,4 +12,6 @@ com.fr.design.web.pane.text.field=450*20
com.fr.design.actions.server.dialog=700*630
com.fr.design.report.fit.templatePane.dialog=600*400
com.fr.design.report.fit.firstColumn=80*20
-com.fr.design.report.fit.column=100*20
\ No newline at end of file
+com.fr.design.report.fit.column=100*20
+com.fr.design.lock.LockInfoDialog=400*160
+com.fr.design.mainframe.ForbiddenPane.refreshButton=68*24
\ No newline at end of file
diff --git a/designer-base/src/main/resources/com/fr/design/i18n/dimension_zh_TW.properties b/designer-base/src/main/resources/com/fr/design/i18n/dimension_zh_TW.properties
index 8ea7fd7c26..5813cd96cc 100644
--- a/designer-base/src/main/resources/com/fr/design/i18n/dimension_zh_TW.properties
+++ b/designer-base/src/main/resources/com/fr/design/i18n/dimension_zh_TW.properties
@@ -11,4 +11,6 @@ com.fr.design.web.pane.text.field=450*20
com.fr.design.actions.server.dialog=700*630
com.fr.design.report.fit.templatePane.dialog=600*400
com.fr.design.report.fit.firstColumn=80*20
-com.fr.design.report.fit.column=100*20
\ No newline at end of file
+com.fr.design.report.fit.column=100*20
+com.fr.design.lock.LockInfoDialog=400*160
+com.fr.design.mainframe.ForbiddenPane.refreshButton=68*24
\ No newline at end of file
diff --git a/designer-base/src/main/resources/com/fr/design/javascript/jsapi/category.json b/designer-base/src/main/resources/com/fr/design/javascript/jsapi/category.json
new file mode 100644
index 0000000000..ad17929765
--- /dev/null
+++ b/designer-base/src/main/resources/com/fr/design/javascript/jsapi/category.json
@@ -0,0 +1,47 @@
+{
+ "Fine-Design_JSAPI_Public_Module": {
+ "Fine-Design_JSAPI_Public_Module_Global": {
+ "Fine-Design_JSAPI_Public_Module_Global_Universal": {},
+ "Fine-Design_JSAPI_Public_Module_Global_FR": {},
+ "Fine-Design_JSAPI_Public_Module_Global_FS": {},
+ "Fine-Design_JSAPI_Public_Module_Global_Mobile": {}
+ },
+ "Fine-Design_JSAPI_Public_Module_Widget": {
+ "Fine-Design_JSAPI_Public_Module_Widget_Get": {},
+ "Fine-Design_JSAPI_Public_Module_Widget_Universal": {},
+ "Fine-Design_JSAPI_Public_Module_Date_Widget_Peculiar": {},
+ "Fine-Design_JSAPI_Public_Module_Button_Widget_Peculiar": {},
+ "Fine-Design_JSAPI_Public_Module_Combobox_Widget_Peculiar": {}
+ },
+ "Fine-Design_JSAPI_Public_Module_Table": {
+ "Fine-Design_JSAPI_Public_Module_Table_Marquee": {},
+ "Fine-Design_JSAPI_Public_Module_Table_Scrollbar": {},
+ "Fine-Design_JSAPI_Public_Module_Table_Cell_Style": {},
+ "Fine-Design_JSAPI_Public_Module_Table_Row_Height_Col_Width": {},
+ "Fine-Design_JSAPI_Public_Module_Table_Cell_Value": {},
+ "Fine-Design_JSAPI_Public_Module_Table_Cell_Radius": {}
+ },
+ "Fine-Design_JSAPI_Public_Module_Toolbar": {
+ "Fine-Design_JSAPI_Public_Module_Toolbar_Email_Button": {}
+ },
+ "Fine-Design_JSAPI_Public_Module_Report_Page": {
+ "Fine-Design_JSAPI_Public_Module_Report_Page_Jump": {},
+ "Fine-Design_JSAPI_Public_Module_Report_Page_Number_Get": {}
+ },
+ "Fine-Design_JSAPI_Public_Module_Report_Export": {}
+ },
+ "Fine-Design_JSAPI_Cpt": {
+ "Fine-Design_JSAPI_Cpt_Page_Preview": {
+ "Fine-Design_JSAPI_Cpt_Page_Preview_Folding_Tree": {}
+ },
+ "Fine-Design_JSAPI_Cpt_Write_Preview": {},
+ "Fine-Design_JSAPI_Cpt_View_Preview": {
+ "Fine-Design_JSAPI_Cpt_View_Preview_Report_Location": {}
+ }
+ },
+ "Fine-Design_JSAPI_Form": {
+ "Fine-Design_JSAPI_Form_Component_Get": {},
+ "Fine-Design_JSAPI_Form_Component_Universal": {},
+ "Fine-Design_JSAPI_Form_Component_Tab": {}
+ }
+}
\ No newline at end of file
diff --git a/designer-base/src/main/resources/com/fr/design/javascript/jsapi/jsapi.json b/designer-base/src/main/resources/com/fr/design/javascript/jsapi/jsapi.json
new file mode 100644
index 0000000000..e268f70e18
--- /dev/null
+++ b/designer-base/src/main/resources/com/fr/design/javascript/jsapi/jsapi.json
@@ -0,0 +1,33 @@
+{
+ "Fine-Design_JSAPI_Public_Module_Global_Universal": ["_g()", "getParameterContainer", "parameterCommit", "loadContentPane", "getPreviewType"],
+ "Fine-Design_JSAPI_Public_Module_Global_FR": [ "servletURL", "serverURL", "server", "fineServletURL", "SessionMgr.getSessionID", "showDialog", "closeDialog",
+ "doHyperlinkByGet", "doHyperlinkByPost", "doURLPrint", "Msg", "remoteEvaluate", "jsonEncode", "jsonDecode",
+ "ajax", "isEmpty", "isArray", "cellStr2ColumnRow", "columnRow2CellStr"],
+ "Fine-Design_JSAPI_Public_Module_Global_FS": ["signOut", "tabPane.closeActiveTab", "tabPane.addItem"],
+ "Fine-Design_JSAPI_Public_Module_Global_Mobile": ["location", "Mobile.getDeviceInfo"],
+ "Fine-Design_JSAPI_Public_Module_Widget_Get": ["this", "this.options.form", "getWidgetByName"],
+ "Fine-Design_JSAPI_Public_Module_Widget_Universal": ["getValue", "getText", "setValue", "visible", "invisible", "setVisible", "isVisible", "setEnable", "isEnabled",
+ "reset", "getType", "setWaterMark", "fireEvent", "setPopupStyle"],
+ "Fine-Design_JSAPI_Public_Module_Date_Widget_Peculiar":["setMaxAndMinDate"],
+ "Fine-Design_JSAPI_Public_Module_Button_Widget_Peculiar":["doClick"],
+ "Fine-Design_JSAPI_Public_Module_Combobox_Widget_Peculiar":["setName4Empty"],
+ "Fine-Design_JSAPI_Public_Module_Table_Marquee":["startMarquee", "stopMarquee"],
+ "Fine-Design_JSAPI_Public_Module_Table_Scrollbar":["setHScrollBarVisible", "setVScrollBarVisible"],
+ "Fine-Design_JSAPI_Public_Module_Table_Cell_Style":["addEffect"],
+ "Fine-Design_JSAPI_Public_Module_Table_Row_Height_Col_Width":["setRowHeight", "setColWidth"],
+ "Fine-Design_JSAPI_Public_Module_Table_Cell_Value":["getCellValue", "setCellValue"],
+ "Fine-Design_JSAPI_Public_Module_Table_Cell_Radius":["setCellRadius"],
+ "Fine-Design_JSAPI_Public_Module_Toolbar":["toolBarFloat", "setStyle","getToolbar"],
+ "Fine-Design_JSAPI_Public_Module_Toolbar_Email_Button":["changeFormat"],
+ "Fine-Design_JSAPI_Public_Module_Report_Page_Jump":["gotoPreviousPage", "gotoNextPage", "gotoLastPage", "gotoFirstPage", "gotoPage"],
+ "Fine-Design_JSAPI_Public_Module_Report_Page_Number_Get":["getCurrentPageIndex", "getReportTotalPage", "currentPageIndex", "reportTotalPage"],
+ "Fine-Design_JSAPI_Public_Module_Report_Export":["exportReportToExcel", "exportReportToImage", "exportReportToPDF", "exportReportToWord"],
+ "Fine-Design_JSAPI_Cpt_Page_Preview_Folding_Tree":["expandNodeLayer", "collapseNodeLayer", "expandAllNodeLayer", "collapseAllNodeLayer"],
+ "Fine-Design_JSAPI_Cpt_Write_Preview":["getWidgetByCell", "appendReportRC", "appendReportRow",
+ "deleteReportRC", "deleteRows", "refreshAllSheets", "loadSheetByIndex", "loadSheetByName", "isDirtyPage",
+ "isAutoStash", "writeReport", "verifyAndWriteReport", "verifyReport", "importExcel", "importExcel_Clean",
+ "importExcel_Append", "importExcel_Cover", "stash", "clear"],
+ "Fine-Design_JSAPI_Cpt_View_Preview_Report_Location":["centerReport"],
+ "Fine-Design_JSAPI_Form_Component_Get":["getAllWidgets"],
+ "Fine-Design_JSAPI_Form_Component_Tab":["showCardByIndex", "showCardByIndex", "getShowIndex", "setTitleVisible"]
+}
\ No newline at end of file
diff --git a/designer-base/src/test/java/com/fr/design/javascript/jsapi/JSAPITreeHelperTest.java b/designer-base/src/test/java/com/fr/design/javascript/jsapi/JSAPITreeHelperTest.java
new file mode 100644
index 0000000000..e0d5ae7c9e
--- /dev/null
+++ b/designer-base/src/test/java/com/fr/design/javascript/jsapi/JSAPITreeHelperTest.java
@@ -0,0 +1,31 @@
+package com.fr.design.javascript.jsapi;
+
+import java.util.List;
+import javax.swing.tree.DefaultMutableTreeNode;
+import junit.framework.TestCase;
+
+public class JSAPITreeHelperTest extends TestCase {
+ public void testGetName(){
+ List names = JSAPITreeHelper.getNames("Fine-Design_JSAPI_Public_Module_Toolbar");
+ assertEquals(names.size(),4);
+ assertTrue(names.contains( "toolBarFloat"));
+ assertTrue(names.contains( "setStyle"));
+ assertTrue(names.contains( "getToolbar"));
+ assertTrue(names.contains( "changeFormat"));
+ List allNames = JSAPITreeHelper.getAllNames();
+ assertEquals(allNames.size(),16);
+ }
+
+ public void testGetDirectCategory(){
+ String directCategory = JSAPITreeHelper.getDirectCategory("_g()");
+ assertEquals(directCategory,"Fine-Design_JSAPI_Public_Module_Global_Universal");
+ directCategory = JSAPITreeHelper.getDirectCategory("showCardByIndex");
+ assertEquals(directCategory,"Fine-Design_JSAPI_Form_Component_Tab");
+ }
+
+ public void testCreateJSAPITree(){
+ DefaultMutableTreeNode rootNode = new DefaultMutableTreeNode();
+ JSAPITreeHelper.createJSAPITree(rootNode);
+ assertEquals(2,rootNode.getChildCount());
+ }
+}
diff --git a/designer-base/src/test/resources/com/fr/design/javascript/jsapi/category.json b/designer-base/src/test/resources/com/fr/design/javascript/jsapi/category.json
new file mode 100644
index 0000000000..2d0e50ba02
--- /dev/null
+++ b/designer-base/src/test/resources/com/fr/design/javascript/jsapi/category.json
@@ -0,0 +1,17 @@
+{
+ "Fine-Design_JSAPI_Public_Module": {
+ "Fine-Design_JSAPI_Public_Module_Global": {
+ "Fine-Design_JSAPI_Public_Module_Global_Universal": {},
+ "Fine-Design_JSAPI_Public_Module_Global_Mobile": {}
+ },
+ "Fine-Design_JSAPI_Public_Module_Widget": {
+ "Fine-Design_JSAPI_Public_Module_Date_Widget_Peculiar": {}
+ },
+ "Fine-Design_JSAPI_Public_Module_Toolbar": {
+ "Fine-Design_JSAPI_Public_Module_Toolbar_Email_Button": {}
+ }
+ },
+ "Fine-Design_JSAPI_Form": {
+ "Fine-Design_JSAPI_Form_Component_Tab": {}
+ }
+}
\ No newline at end of file
diff --git a/designer-base/src/test/resources/com/fr/design/javascript/jsapi/jsapi.json b/designer-base/src/test/resources/com/fr/design/javascript/jsapi/jsapi.json
new file mode 100644
index 0000000000..4ff0a321ed
--- /dev/null
+++ b/designer-base/src/test/resources/com/fr/design/javascript/jsapi/jsapi.json
@@ -0,0 +1,8 @@
+{
+ "Fine-Design_JSAPI_Public_Module_Global_Universal": ["_g()", "getParameterContainer", "parameterCommit", "loadContentPane", "getPreviewType"],
+ "Fine-Design_JSAPI_Public_Module_Global_Mobile": ["location", "Mobile.getDeviceInfo"],
+ "Fine-Design_JSAPI_Public_Module_Date_Widget_Peculiar":["setMaxAndMinDate"],
+ "Fine-Design_JSAPI_Public_Module_Toolbar":["toolBarFloat", "setStyle","getToolbar"],
+ "Fine-Design_JSAPI_Public_Module_Toolbar_Email_Button":["changeFormat"],
+ "Fine-Design_JSAPI_Form_Component_Tab":["showCardByIndex", "showCardByIndex", "getShowIndex", "setTitleVisible"]
+}
\ No newline at end of file
diff --git a/designer-form/src/main/java/com/fr/design/designer/creator/XLayoutContainer.java b/designer-form/src/main/java/com/fr/design/designer/creator/XLayoutContainer.java
index 01d09fe4cc..3d0b24847b 100644
--- a/designer-form/src/main/java/com/fr/design/designer/creator/XLayoutContainer.java
+++ b/designer-form/src/main/java/com/fr/design/designer/creator/XLayoutContainer.java
@@ -474,14 +474,6 @@ public abstract class XLayoutContainer extends XBorderStyleWidgetCreator impleme
return 0;
}
- /**
- * 切换到非添加状态
- *
- * @param designer 表单设计器
- */
- public void stopAddingState(FormDesigner designer) {
- }
-
/**
* 寻找最近的为自适应布局的父容器
*
diff --git a/designer-form/src/main/java/com/fr/design/designer/creator/cardlayout/XWCardMainBorderLayout.java b/designer-form/src/main/java/com/fr/design/designer/creator/cardlayout/XWCardMainBorderLayout.java
index fdc01be1e9..e09b31fb09 100644
--- a/designer-form/src/main/java/com/fr/design/designer/creator/cardlayout/XWCardMainBorderLayout.java
+++ b/designer-form/src/main/java/com/fr/design/designer/creator/cardlayout/XWCardMainBorderLayout.java
@@ -179,18 +179,6 @@ public class XWCardMainBorderLayout extends XWBorderLayout {
}
}
- /**
- * 切换到非添加状态
- *
- * @return designer 表单设计器
- */
- @Override
- public void stopAddingState(FormDesigner designer){
- designer.stopAddingState();
- return;
- }
-
-
/**
* 添加card区域
*
diff --git a/designer-form/src/main/java/com/fr/design/designer/creator/cardlayout/XWCardTagLayout.java b/designer-form/src/main/java/com/fr/design/designer/creator/cardlayout/XWCardTagLayout.java
index ceb334028a..850378af21 100644
--- a/designer-form/src/main/java/com/fr/design/designer/creator/cardlayout/XWCardTagLayout.java
+++ b/designer-form/src/main/java/com/fr/design/designer/creator/cardlayout/XWCardTagLayout.java
@@ -187,15 +187,6 @@ public class XWCardTagLayout extends XWHorizontalBoxLayout {
return DEFAULT_NAME;
}
- /**
- * 切换到非添加状态
- *
- * @return designer 表单设计器
- */
- @Override
- public void stopAddingState(FormDesigner designer) {
- designer.stopAddingState();
- }
//新增时去tabFitLayout名字中最大的Index+1,防止重名
private int getTabNameIndex() {
diff --git a/designer-form/src/main/java/com/fr/design/designer/creator/cardlayout/XWCardTitleLayout.java b/designer-form/src/main/java/com/fr/design/designer/creator/cardlayout/XWCardTitleLayout.java
index 7cdca0a0bc..9695eee18a 100644
--- a/designer-form/src/main/java/com/fr/design/designer/creator/cardlayout/XWCardTitleLayout.java
+++ b/designer-form/src/main/java/com/fr/design/designer/creator/cardlayout/XWCardTitleLayout.java
@@ -166,18 +166,7 @@ public class XWCardTitleLayout extends XWBorderLayout {
XWCardTagLayout xwCardTagLayout = (XWCardTagLayout) this.getComponent(0);
this.addTagPart(xwCardTagLayout);
}
-
- /**
- * 切换到非添加状态
- *
- * @return designer 表单设计器
- */
- @Override
- public void stopAddingState(FormDesigner designer){
- designer.stopAddingState();
- return;
- }
-
+
/**
* 该布局隐藏,无需对边框进行操作
* @param border 边框
diff --git a/designer-form/src/main/java/com/fr/design/fit/FormFitAttrAction.java b/designer-form/src/main/java/com/fr/design/fit/FormFitAttrAction.java
new file mode 100644
index 0000000000..dad6e9be9c
--- /dev/null
+++ b/designer-form/src/main/java/com/fr/design/fit/FormFitAttrAction.java
@@ -0,0 +1,85 @@
+package com.fr.design.fit;
+
+import com.fr.design.actions.JTemplateAction;
+import com.fr.design.beans.BasicBeanPane;
+import com.fr.design.dialog.DialogActionAdapter;
+import com.fr.design.dialog.UIDialog;
+import com.fr.design.i18n.Toolkit;
+import com.fr.design.mainframe.DesignerContext;
+import com.fr.design.mainframe.JForm;
+import com.fr.design.mainframe.JTemplate;
+import com.fr.design.menu.MenuKeySet;
+import com.fr.design.report.fit.FormFitAttrModelType;
+import com.fr.form.main.Form;
+import com.fr.report.fit.FitProvider;
+import com.fr.report.fit.ReportFitAttr;
+
+import javax.swing.KeyStroke;
+import java.awt.Dimension;
+import java.awt.event.ActionEvent;
+
+public class FormFitAttrAction extends JTemplateAction {
+ private static final MenuKeySet REPORT_FIT_ATTR = new MenuKeySet() {
+ @Override
+ public char getMnemonic() {
+ return 'T';
+ }
+
+ @Override
+ public String getMenuName() {
+ return Toolkit.i18nText("Fine-Designer_PC_Fit_Attr");
+ }
+
+ @Override
+ public KeyStroke getKeyStroke() {
+ return null;
+ }
+ };
+
+ public FormFitAttrAction(JTemplate jTemplate) {
+ super(jTemplate);
+ initMenuStyle();
+ }
+
+ private void initMenuStyle() {
+ this.setMenuKeySet(REPORT_FIT_ATTR);
+ this.setName(getMenuKeySet().getMenuKeySetName() + "...");
+ this.setMnemonic(getMenuKeySet().getMnemonic());
+ this.setSmallIcon("/com/fr/design/images/reportfit/fit");
+ }
+
+ /**
+ * Action触发事件
+ *
+ * @param e 事件
+ */
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ final JTemplate jwb = getEditingComponent();
+ if (jwb == null || !(jwb.getTarget() instanceof Form)) {
+ return;
+ }
+ JForm jForm = (JForm) jwb;
+ Form wbTpl = jForm.getTarget();
+ ReportFitAttr fitAttr = wbTpl.getReportFitAttr();
+ FormFitAttrPane formFitAttrPane = new FormFitAttrPane(jForm, FormFitAttrModelType.parse(wbTpl));
+ showReportFitDialog(fitAttr, jwb, wbTpl, formFitAttrPane);
+ }
+
+ private void showReportFitDialog(ReportFitAttr fitAttr, final JTemplate jwb, final FitProvider wbTpl, final BasicBeanPane attrPane) {
+ attrPane.populateBean(fitAttr);
+ UIDialog dialog = attrPane.showWindowWithCustomSize(DesignerContext.getDesignerFrame(), new DialogActionAdapter() {
+ @Override
+ public void doOk() {
+ fireEditingOk(jwb, wbTpl, attrPane.updateBean(), fitAttr);
+ }
+ }, new Dimension(660, 600));
+ dialog.setVisible(true);
+ }
+
+ private void fireEditingOk(final JTemplate jwb, final FitProvider wbTpl, ReportFitAttr newReportFitAttr, ReportFitAttr oldReportFitAttr) {
+ wbTpl.setReportFitAttr(newReportFitAttr);
+ jwb.fireTargetModified();
+ }
+
+}
diff --git a/designer-form/src/main/java/com/fr/design/fit/FormFitAttrPane.java b/designer-form/src/main/java/com/fr/design/fit/FormFitAttrPane.java
new file mode 100644
index 0000000000..51eda6d36e
--- /dev/null
+++ b/designer-form/src/main/java/com/fr/design/fit/FormFitAttrPane.java
@@ -0,0 +1,377 @@
+package com.fr.design.fit;
+
+import com.fr.design.beans.BasicBeanPane;
+import com.fr.design.designer.IntervalConstants;
+import com.fr.design.designer.creator.XCreator;
+import com.fr.design.designer.creator.XLayoutContainer;
+import com.fr.design.designer.creator.XOccupiedLayout;
+import com.fr.design.designer.creator.XWAbsoluteBodyLayout;
+import com.fr.design.designer.creator.XWFitLayout;
+import com.fr.design.designer.creator.XWScaleLayout;
+import com.fr.design.designer.properties.items.FRLayoutTypeItems;
+import com.fr.design.designer.properties.items.Item;
+import com.fr.design.dialog.FineJOptionPane;
+import com.fr.design.gui.icombobox.UIComboBox;
+import com.fr.design.gui.ilable.UILabel;
+import com.fr.design.i18n.Toolkit;
+import com.fr.design.layout.FRGUIPaneFactory;
+import com.fr.design.layout.TableLayoutHelper;
+import com.fr.design.mainframe.FormDesigner;
+import com.fr.design.mainframe.FormSelectionUtils;
+import com.fr.design.mainframe.JForm;
+import com.fr.design.mainframe.WidgetPropertyPane;
+import com.fr.design.report.fit.FitType;
+import com.fr.design.report.fit.FormFitAttrModelType;
+import com.fr.design.report.fit.FormFitConfigPane;
+import com.fr.design.report.fit.ReportFitConfigPane;
+import com.fr.design.widget.FRWidgetFactory;
+import com.fr.form.main.Form;
+import com.fr.form.ui.Widget;
+import com.fr.form.ui.container.WAbsoluteBodyLayout;
+import com.fr.form.ui.container.WAbsoluteLayout;
+import com.fr.form.ui.container.WBodyLayoutType;
+import com.fr.form.ui.container.WFitLayout;
+import com.fr.form.ui.container.WSortLayout;
+import com.fr.general.ComparatorUtils;
+import com.fr.general.act.BorderPacker;
+import com.fr.log.FineLoggerFactory;
+import com.fr.report.fit.ReportFitAttr;
+
+import javax.swing.BorderFactory;
+import javax.swing.DefaultComboBoxModel;
+import javax.swing.JPanel;
+import javax.swing.SwingConstants;
+import java.awt.BorderLayout;
+import java.awt.Component;
+import java.awt.Dimension;
+import java.awt.Rectangle;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.ItemEvent;
+import java.awt.event.ItemListener;
+
+import static com.fr.design.i18n.Toolkit.i18nText;
+import static javax.swing.JOptionPane.*;
+
+public class FormFitAttrPane extends BasicBeanPane {
+
+ private UIComboBox layoutComboBox;
+ private UIComboBox scaleComboBox;
+ private FormFitAttrModelType fitAttrModelType;
+
+ protected UIComboBox itemChoose;
+
+ private JForm jForm;
+ private ReportFitConfigPane fitConfigPane;
+
+ public FormFitAttrPane(JForm jForm, FormFitAttrModelType fitAttrModelType) {
+ this.fitAttrModelType = fitAttrModelType;
+ this.jForm = jForm;
+ initComponents();
+ }
+
+
+ private void initComponents() {
+ this.setLayout(FRGUIPaneFactory.createBorderLayout());
+ this.setBorder(BorderFactory.createEmptyBorder(12, 5, 0, 5));
+ this.add(createReportFitSettingPane(), BorderLayout.CENTER);
+ this.add(createReportLayoutSettingPane(), BorderLayout.NORTH);
+
+ }
+
+
+ private JPanel createReportLayoutSettingPane() {
+ JPanel jPanel = FRGUIPaneFactory.createTitledBorderPane(Toolkit.i18nText("Fine-Design_Form_PC_Fit_Config_Layout"));
+ jPanel.add(createAreaScalePane(), BorderLayout.CENTER);
+ jPanel.setPreferredSize(new Dimension(640, 84));
+ return jPanel;
+ }
+
+ protected String[] getItemNames() {
+ return new String[]{Toolkit.i18nText("Fine-Design_Report_Using_Server_Report_View_Settings"),
+ Toolkit.i18nText("Fine-Design_Report_I_Want_To_Set_Single")};
+ }
+
+
+ private JPanel createReportFitSettingPane() {
+ JPanel jPanel = FRGUIPaneFactory.createTitledBorderPane(Toolkit.i18nText("Fine-Design_Form_PC_Fit_Config_Content_Attr"));
+ JPanel contentPane = FRGUIPaneFactory.createBorderLayout_S_Pane();
+ jPanel.add(contentPane, BorderLayout.CENTER);
+ UILabel label = new UILabel(Toolkit.i18nText("Fine-Design_Form_PC_Fit_Config_Settings"));
+ label.setBorder(BorderFactory.createEmptyBorder(3, 0, 0, 0));
+ contentPane.add(label, BorderLayout.WEST);
+ label.setPreferredSize(new Dimension(100, 0));
+ label.setVerticalAlignment(SwingConstants.TOP);
+ itemChoose = new UIComboBox(getItemNames());
+ itemChoose.setPreferredSize(new Dimension(160, 20));
+ Form form = jForm.getTarget();
+ itemChoose.addItemListener(new ItemListener() {
+ @Override
+ public void itemStateChanged(ItemEvent e) {
+ if (e.getStateChange() == ItemEvent.SELECTED) {
+ if (isTemplateSingleSet()) {
+ if (form != null) {
+ ReportFitAttr fitAttr = form.getReportFitAttr();
+ populate(fitAttr);
+ }
+ } else {
+ populate(fitAttrModelType.getFitAttrModel().getGlobalReportFitAttr());
+ }
+ }
+ }
+ });
+ JPanel centerPane = FRGUIPaneFactory.createVerticalFlowLayout_S_Pane(true);
+ centerPane.add(itemChoose);
+ centerPane.add(fitConfigPane = new FormFitConfigPane(this.fitAttrModelType.getFitAttrModel()));
+ contentPane.add(centerPane, BorderLayout.CENTER);
+ return jPanel;
+ }
+
+ public void populate(ReportFitAttr reportFitAttr) {
+ if (reportFitAttr == null) {
+ reportFitAttr = fitAttrModelType.getFitAttrModel().getGlobalReportFitAttr();
+ }
+
+ this.setEnabled(isTemplateSingleSet());
+ fitConfigPane.populateBean(reportFitAttr);
+ }
+
+
+ public ReportFitAttr updateBean() {
+ updateLayoutType();
+ if (!isTemplateSingleSet()) {
+ return null;
+ } else {
+ return fitConfigPane.updateBean();
+ }
+ }
+
+ private void updateLayoutType() {
+ XLayoutContainer xLayoutContainer = this.jForm.getRootComponent();
+ if (xLayoutContainer == null || !xLayoutContainer.acceptType(XWFitLayout.class)) {
+ return;
+ }
+ XWFitLayout xwFitLayout = (XWFitLayout) xLayoutContainer;
+ WFitLayout wFitLayout = xwFitLayout.toData();
+ int state = layoutComboBox.getSelectedIndex();
+ WBodyLayoutType selectType = WBodyLayoutType.parse(state);
+ if (selectType != wFitLayout.getBodyLayoutType()) {
+ wFitLayout.setLayoutType(selectType);
+ //从自适应布局切换到绝对布局
+ if (selectType == WBodyLayoutType.ABSOLUTE) {
+ switchLayoutFromFit2Absolute(xwFitLayout);
+ } else {
+ //从绝对布局切换到自适应布局
+ switchLayoutFromAbsolute2Fit(xwFitLayout);
+ }
+ }
+ wFitLayout.setCompatibleScaleAttr(fitAttrModelType.parseScaleAttrFromShowIndex(this.scaleComboBox.getSelectedIndex(), wFitLayout.getBodyLayoutType()));
+ }
+
+
+ private void switchLayoutFromFit2Absolute(XWFitLayout xWFitLayout) {
+ try {
+ WFitLayout layout = xWFitLayout.toData();
+ WAbsoluteBodyLayout wAbsoluteBodyLayout = new WAbsoluteBodyLayout("body");
+ wAbsoluteBodyLayout.setCompState(WAbsoluteLayout.STATE_FIXED);
+ // 切换布局类型时,保留body背景样式
+ wAbsoluteBodyLayout.setBorderStyleFollowingTheme(layout.isBorderStyleFollowingTheme());
+ wAbsoluteBodyLayout.setBorderStyle((BorderPacker) (layout.getBorderStyle().clone()));
+ Component[] components = xWFitLayout.getComponents();
+ Rectangle[] backupBounds = getBackupBoundsFromFitLayout(xWFitLayout);
+ xWFitLayout.removeAll();
+ layout.resetStyle();
+ XWAbsoluteBodyLayout xwAbsoluteBodyLayout = xWFitLayout.getBackupParent() == null ? new XWAbsoluteBodyLayout(wAbsoluteBodyLayout, new Dimension(0, 0)) : (XWAbsoluteBodyLayout) xWFitLayout.getBackupParent();
+ xWFitLayout.setFixLayout(false);
+ xWFitLayout.getLayoutAdapter().addBean(xwAbsoluteBodyLayout, 0, 0);
+ for (int i = 0; i < components.length; i++) {
+ XCreator xCreator = (XCreator) components[i];
+ xCreator.setBounds(backupBounds[i]);
+ //部分控件被ScaleLayout包裹着,绝对布局里面要放出来
+ if (xCreator.acceptType(XWScaleLayout.class)) {
+ if (xCreator.getComponentCount() > 0 && ((XCreator) xCreator.getComponent(0)).shouldScaleCreator()) {
+ Component component = xCreator.getComponent(0);
+ component.setBounds(xCreator.getBounds());
+ }
+ }
+ if (!xCreator.acceptType(XOccupiedLayout.class)) {
+ xwAbsoluteBodyLayout.add(xCreator);
+ }
+
+ }
+ copyLayoutAttr(layout, xwAbsoluteBodyLayout.toData());
+ xWFitLayout.setBackupParent(xwAbsoluteBodyLayout);
+ FormDesigner formDesigner = WidgetPropertyPane.getInstance().getEditingFormDesigner();
+ formDesigner.getSelectionModel().setSelectedCreators(
+ FormSelectionUtils.rebuildSelection(xWFitLayout, new Widget[]{wAbsoluteBodyLayout}));
+ if (xwAbsoluteBodyLayout.toData() != null) {
+ xwAbsoluteBodyLayout.toData().setBorderStyleFollowingTheme(wAbsoluteBodyLayout.isBorderStyleFollowingTheme());
+ xwAbsoluteBodyLayout.toData().setBorderStyle(wAbsoluteBodyLayout.getBorderStyle());
+ }
+ xwAbsoluteBodyLayout.refreshStylePreviewEffect();
+ if (xWFitLayout.toData() != null) {
+ xWFitLayout.toData().resetStyle();
+ }
+ xWFitLayout.refreshStylePreviewEffect();
+ formDesigner.switchBodyLayout(xwAbsoluteBodyLayout);
+ } catch (Exception e) {
+ FineLoggerFactory.getLogger().error(e.getMessage(), e);
+
+ }
+ }
+
+ private Rectangle[] getBackupBoundsFromFitLayout(XWFitLayout xWFitLayout) {
+ int count = xWFitLayout.getComponentCount();
+ Rectangle[] rectangles = new Rectangle[count];
+ for (int i = 0; i < count; i++) {
+ rectangles[i] = xWFitLayout.getComponent(i).getBounds();
+ }
+ return rectangles;
+ }
+
+ protected void copyLayoutAttr(WSortLayout srcLayout, WSortLayout destLayout) {
+ destLayout.clearListeners();
+ destLayout.clearMobileWidgetList();
+ for (int i = 0, len = srcLayout.getMobileWidgetListSize(); i < len; i++) {
+ destLayout.addMobileWidget(srcLayout.getMobileWidget(i));
+ }
+ destLayout.setSorted(true);
+ for (int i = 0, len = srcLayout.getListenerSize(); i < len; i++) {
+ destLayout.addListener(srcLayout.getListener(i));
+ }
+ srcLayout.clearListeners();
+ srcLayout.clearMobileWidgetList();
+ }
+
+
+ private void switchLayoutFromAbsolute2Fit(XWFitLayout xwFitLayout) {
+ XWAbsoluteBodyLayout xwAbsoluteBodyLayout = getAbsoluteBodyLayout(xwFitLayout);
+ if (xwAbsoluteBodyLayout == null) {
+ return;
+ }
+ WAbsoluteBodyLayout layout = xwAbsoluteBodyLayout.toData();
+ WFitLayout wFitLayout = xwFitLayout.toData();
+ wFitLayout.resetStyle();
+ xwFitLayout.switch2FitBodyLayout(xwAbsoluteBodyLayout);
+ // 切换布局类型时,保留body背景样式
+ if (wFitLayout != null) {
+ wFitLayout.setBorderStyleFollowingTheme(layout.isBorderStyleFollowingTheme());
+ wFitLayout.setBorderStyle(layout.getBorderStyle());
+ }
+ copyLayoutAttr(layout, xwFitLayout.toData());
+
+ copyLayoutAttr(layout, xwFitLayout.toData());
+ xwFitLayout.refreshStylePreviewEffect();
+ }
+
+ private XWAbsoluteBodyLayout getAbsoluteBodyLayout(XWFitLayout xwFitLayout) {
+ if (xwFitLayout != null && xwFitLayout.getComponentCount() > 0) {
+ Component component = xwFitLayout.getComponent(0);
+ if (component instanceof XWAbsoluteBodyLayout) {
+ return (XWAbsoluteBodyLayout) component;
+ }
+ }
+ return null;
+ }
+
+ private JPanel createAreaScalePane() {
+ initLayoutComboBox();
+
+ UILabel layoutTypeLabel = FRWidgetFactory.createLineWrapLabel(com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Form_Attr_Layout_Type"));
+ UILabel scaleModeLabel = FRWidgetFactory.createLineWrapLabel(com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Form_PC_Fit_Config_Scale_Setting"));
+ Component[][] components = new Component[][]{
+ {layoutTypeLabel, layoutComboBox},
+ {scaleModeLabel, scaleComboBox}
+ };
+ JPanel contentPane = TableLayoutHelper.createGapTableLayoutPane(components,
+ TableLayoutHelper.FILL_LASTCOLUMN, 20, IntervalConstants.INTERVAL_L1);
+ JPanel containerPane = FRGUIPaneFactory.createBorderLayout_S_Pane();
+ containerPane.add(contentPane, BorderLayout.CENTER);
+
+ return containerPane;
+ }
+
+
+ public void initLayoutComboBox() {
+ Item[] items = FRLayoutTypeItems.ITEMS;
+ DefaultComboBoxModel model = new DefaultComboBoxModel();
+ for (Item item : items) {
+ model.addElement(item);
+ }
+ scaleComboBox = new UIComboBox(model);
+ scaleComboBox.setModel(new DefaultComboBoxModel(fitAttrModelType.getFitLayoutScaleAttr()));
+ layoutComboBox = new UIComboBox(model);
+ layoutComboBox.setPreferredSize(new Dimension(160, 20));
+ scaleComboBox.setPreferredSize(new Dimension(160, 20));
+ WFitLayout wFitLayout = jForm.getTarget().getWFitLayout();
+ layoutComboBox.addActionListener(new ActionListener() {
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ int selectIndex = layoutComboBox.getSelectedIndex();
+ if (selectIndex == 0) {
+ if (wFitLayout.getBodyLayoutType() == WBodyLayoutType.ABSOLUTE) {
+ int selVal = FineJOptionPane.showConfirmDialog(
+ FormFitAttrPane.this,
+ Toolkit.i18nText("Fine-Design_Form_Layout_Switch_Tip"),
+ Toolkit.i18nText("Fine-Design_Basic_Alert"),
+ OK_CANCEL_OPTION,
+ WARNING_MESSAGE
+ );
+ if (OK_OPTION != selVal) {
+ layoutComboBox.setSelectedIndex(1);
+ return;
+ }
+ }
+ scaleComboBox.setModel(new DefaultComboBoxModel(fitAttrModelType.getFitLayoutScaleAttr()));
+ } else {
+ scaleComboBox.setModel(new DefaultComboBoxModel(fitAttrModelType.getAbsoluteLayoutSaleAttr()));
+ }
+ scaleComboBox.setSelectedIndex(0);
+ }
+ });
+
+ scaleComboBox.addActionListener(new ActionListener() {
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ WBodyLayoutType selectBodyType = WBodyLayoutType.parse(layoutComboBox.getSelectedIndex());
+ int state = fitAttrModelType.parseScaleAttrFromShowIndex(scaleComboBox.getSelectedIndex(), selectBodyType);
+ fitConfigPane.refreshPreviewJPanel(FitType.parseByFitState(state));
+ }
+ });
+ }
+
+
+ @Override
+ public void populateBean(ReportFitAttr reportFitAttr) {
+ WFitLayout wFitLayout = jForm.getTarget().getWFitLayout();
+ layoutComboBox.setSelectedIndex(wFitLayout.getBodyLayoutType().getTypeValue());
+ scaleComboBox.setSelectedIndex(fitAttrModelType.getScaleAttrShowIndex(wFitLayout));
+
+ if (reportFitAttr == null) {
+ itemChoose.setSelectedItem(Toolkit.i18nText("Fine-Design_Report_Using_Server_Report_View_Settings"));
+ } else {
+ itemChoose.setSelectedItem(Toolkit.i18nText("Fine-Design_Report_I_Want_To_Set_Single"));
+ }
+ if (reportFitAttr == null) {
+ reportFitAttr = fitAttrModelType.getFitAttrModel().getGlobalReportFitAttr();
+ }
+ setEnabled(isTemplateSingleSet());
+ fitConfigPane.populateBean(reportFitAttr);
+ }
+
+ private boolean isTemplateSingleSet() {
+ return ComparatorUtils.equals(Toolkit.i18nText("Fine-Design_Report_I_Want_To_Set_Single"), itemChoose.getSelectedItem());
+ }
+
+
+ public void setEnabled(boolean enabled) {
+ super.setEnabled(enabled);
+ fitConfigPane.setEnabled(enabled);
+ }
+
+ @Override
+ protected String title4PopupWindow() {
+ return i18nText("Fine-Designer_PC_Fit_Attr");
+ }
+
+}
diff --git a/designer-form/src/main/java/com/fr/design/mainframe/JForm.java b/designer-form/src/main/java/com/fr/design/mainframe/JForm.java
index 8d2748eb39..ec3852437b 100644
--- a/designer-form/src/main/java/com/fr/design/mainframe/JForm.java
+++ b/designer-form/src/main/java/com/fr/design/mainframe/JForm.java
@@ -14,7 +14,6 @@ import com.fr.base.vcs.DesignerMode;
import com.fr.design.DesignModelAdapter;
import com.fr.design.DesignState;
import com.fr.design.DesignerEnvManager;
-import com.fr.design.ExtraDesignClassManager;
import com.fr.design.actions.FormMobileAttrAction;
import com.fr.design.actions.TemplateParameterAction;
import com.fr.design.actions.core.WorkBookSupportable;
@@ -37,7 +36,7 @@ import com.fr.design.designer.properties.FormWidgetAuthorityEditPane;
import com.fr.design.event.TargetModifiedEvent;
import com.fr.design.event.TargetModifiedListener;
import com.fr.design.file.HistoryTemplateListCache;
-import com.fr.design.fun.FormAdaptiveConfigUIProcessor;
+import com.fr.design.fit.FormFitAttrAction;
import com.fr.design.fun.PreviewProvider;
import com.fr.design.fun.PropertyItemPaneProvider;
import com.fr.design.gui.frpane.HyperlinkGroupPane;
@@ -66,7 +65,6 @@ import com.fr.design.menu.ToolBarDef;
import com.fr.design.parameter.ParameterPropertyPane;
import com.fr.design.preview.FormPreview;
import com.fr.design.preview.MobilePreview;
-import com.fr.design.report.fit.menupane.ReportFitAttrAction;
import com.fr.design.roleAuthority.RolesAlreadyEditedPane;
import com.fr.design.utils.gui.LayoutUtils;
import com.fr.file.FILE;
@@ -597,11 +595,7 @@ public class JForm extends JTemplate