From 85ea1d8082a7a236db34ae7a0b72b2026a38ad63 Mon Sep 17 00:00:00 2001 From: richie Date: Fri, 25 Oct 2019 17:31:56 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E6=96=B0=E7=9A=84=E7=BB=84?= =?UTF-8?q?=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../fanruan/api/design/macro/UIConstants.java | 13 + .../api/design/ui/component/UIButton.java | 4 + .../design/ui/component/UIColorButton.java | 231 +++++++ .../design/ui/component/UIToggleButton.java | 245 +++++++ .../design/ui/component/pane/UITextPane.java | 29 + .../api/design/work/RichTextEditingPane.java | 188 ++++++ .../api/design/work/RichTextToolBar.java | 612 ++++++++++++++++++ .../design/work/component/LineComboBox.java | 133 ++++ .../com/fanruan/api/macro/LineConstants.java | 32 + .../form/category/BaseBorderStyleWidget.java | 21 + .../java/com/fanruan/api/util/GeneralKit.java | 30 + .../java/com/fanruan/api/util/GraphKit.java | 104 +++ 12 files changed, 1642 insertions(+) create mode 100644 src/main/java/com/fanruan/api/design/ui/component/UIColorButton.java create mode 100644 src/main/java/com/fanruan/api/design/ui/component/UIToggleButton.java create mode 100644 src/main/java/com/fanruan/api/design/ui/component/pane/UITextPane.java create mode 100644 src/main/java/com/fanruan/api/design/work/RichTextEditingPane.java create mode 100644 src/main/java/com/fanruan/api/design/work/RichTextToolBar.java create mode 100644 src/main/java/com/fanruan/api/design/work/component/LineComboBox.java create mode 100644 src/main/java/com/fanruan/api/macro/LineConstants.java create mode 100644 src/main/java/com/fanruan/api/report/form/category/BaseBorderStyleWidget.java create mode 100644 src/main/java/com/fanruan/api/util/GraphKit.java diff --git a/src/main/java/com/fanruan/api/design/macro/UIConstants.java b/src/main/java/com/fanruan/api/design/macro/UIConstants.java index bfdf0d9..925b64d 100644 --- a/src/main/java/com/fanruan/api/design/macro/UIConstants.java +++ b/src/main/java/com/fanruan/api/design/macro/UIConstants.java @@ -1,5 +1,8 @@ package com.fanruan.api.design.macro; +import com.fanruan.api.util.IOKit; + +import javax.swing.*; import java.awt.*; /** @@ -21,4 +24,14 @@ public interface UIConstants { * 圆角弧度 */ int ARC = 0; + + /** + * 线条 + */ + Stroke BS = new BasicStroke(1f, BasicStroke.CAP_BUTT, BasicStroke.JOIN_ROUND, 2f, new float[]{3, 1}, 0); + + /** + * 用于表示字体的图标 + */ + Icon FONT_ICON = IOKit.readIcon("/com/fr/design/images/gui/color/foreground.png"); } diff --git a/src/main/java/com/fanruan/api/design/ui/component/UIButton.java b/src/main/java/com/fanruan/api/design/ui/component/UIButton.java index 00d035d..fbc9688 100644 --- a/src/main/java/com/fanruan/api/design/ui/component/UIButton.java +++ b/src/main/java/com/fanruan/api/design/ui/component/UIButton.java @@ -29,4 +29,8 @@ public class UIButton extends com.fr.design.gui.ibutton.UIButton { public UIButton(String text, Icon icon) { super(text, icon); } + + public UIButton(Icon normal, Icon rollOver, Icon pressed) { + super(normal, rollOver, pressed); + } } diff --git a/src/main/java/com/fanruan/api/design/ui/component/UIColorButton.java b/src/main/java/com/fanruan/api/design/ui/component/UIColorButton.java new file mode 100644 index 0000000..6ead8d1 --- /dev/null +++ b/src/main/java/com/fanruan/api/design/ui/component/UIColorButton.java @@ -0,0 +1,231 @@ +package com.fanruan.api.design.ui.component; + +import com.fanruan.api.design.macro.UIConstants; +import com.fanruan.api.design.util.GUICoreKit; +import com.fanruan.api.util.AssistKit; +import com.fanruan.api.util.GeneralKit; +import com.fr.design.event.GlobalNameListener; +import com.fr.design.event.GlobalNameObserver; +import com.fr.design.event.UIObserver; +import com.fr.design.event.UIObserverListener; +import com.fr.design.gui.ibutton.UIButtonUI; +import com.fr.design.gui.ipoppane.PopupHider; +import com.fr.design.style.color.ColorControlWindow; + +import javax.swing.*; +import javax.swing.event.ChangeEvent; +import javax.swing.event.ChangeListener; +import javax.swing.event.EventListenerList; +import java.awt.*; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; + +/** + * @author richie + * @version 10.0 + * Created by richie on 2019/10/25 + */ +public class UIColorButton extends UIButton implements PopupHider, UIObserver, GlobalNameObserver { + + private static final int SIZE = 16; + private static final int SIZE_2 = 2; + private static final int SIZE_4 = 4; + private static final int SIZE_6 = 6; + private static final int POPUP_MENU_SHIFT = -70; + private Color color = Color.BLACK; + private ColorControlWindow popupWin; + private EventListenerList colorChangeListenerList = new EventListenerList(); + private boolean isEventBanned = false; + private String colorButtonName = ""; + private UIObserverListener uiObserverListener; + private GlobalNameListener globalNameListener = null; + + public UIColorButton() { + this(UIConstants.FONT_ICON); + } + + public UIColorButton(Icon icon) { + super(icon); + setUI(getButtonUI()); + addMouseListener(new MouseAdapter() { + @Override + public void mouseClicked(MouseEvent e) { + showPopupMenu(); + } + }); + iniListener(); + } + + private void iniListener() { + if (shouldResponseChangeListener()) { + this.addColorChangeListener(new ChangeListener() { + @Override + public void stateChanged(ChangeEvent e) { + if (uiObserverListener == null) { + return; + } + if (globalNameListener != null && shouldResponseNameListener()) { + globalNameListener.setGlobalName(colorButtonName); + } + uiObserverListener.doChange(); + } + }); + } + } + + private UIButtonUI getButtonUI() { + return new UIButtonUI() { + @Override + protected void paintIcon(Graphics g, JComponent c) { + super.paintIcon(g, c); + AbstractButton b = (AbstractButton) c; + ButtonModel model = b.getModel(); + if (model.isEnabled()) { + g.setColor(UIColorButton.this.getColor()); + } else { + g.setColor(new Color(GeneralKit.filterRGB(UIColorButton.this.getColor().getRGB(), 50))); + } + g.fillRect((b.getWidth() - SIZE) / SIZE_2, b.getHeight() - SIZE_6, SIZE, SIZE_4); + } + }; + } + + public void setEventBanned(boolean isEventBanned) { + this.isEventBanned = isEventBanned; + } + + public void setGlobalName(String name) { + colorButtonName = name; + } + + public Color getColor() { + return color; + } + + public void setColor(Color color) { + if (AssistKit.equals(this.color, color)) { + return; + } + + this.color = color; + hidePopupMenu(); + fireColorStateChanged(); + } + + private void showPopupMenu() { + if (isEventBanned) { + return; + } + + if (popupWin != null && popupWin.isVisible()) { + hidePopupMenu(); + return; + } + + if (!this.isEnabled()) { + return; + } + + popupWin = this.getColorControlWindow(); + + GUICoreKit.showPopupMenu(popupWin, this, POPUP_MENU_SHIFT, this.getSize().height); + } + + /** + * 隐藏popupmenu + */ + public void hidePopupMenu() { + if (popupWin != null) { + popupWin.setVisible(false); + repaint(); + } + + popupWin = null; + } + + private ColorControlWindow getColorControlWindow() { + if (this.popupWin == null) { + this.popupWin = new ColorControlWindow(UIColorButton.this) { + @Override + protected void colorChanged() { + UIColorButton.this.setColor(this.getColor()); + } + + }; + } + return popupWin; + } + + /** + * 添加监听 + * + * @param changeListener 监听列表 + */ + public void addColorChangeListener(ChangeListener changeListener) { + colorChangeListenerList.add(ChangeListener.class, changeListener); + } + + /** + * 移除监听 + * Removes an old ColorChangeListener. + * + * @param changeListener 监听列表 + */ + public void removeColorChangeListener(ChangeListener changeListener) { + colorChangeListenerList.remove(ChangeListener.class, changeListener); + } + + /** + * 颜色状态改变 + */ + public void fireColorStateChanged() { + Object[] listeners = colorChangeListenerList.getListenerList(); + ChangeEvent e = null; + + for (int i = listeners.length - 2; i >= 0; i -= 2) { + if (listeners[i] == ChangeListener.class) { + if (e == null) { + e = new ChangeEvent(this); + } + ((ChangeListener) listeners[i + 1]).stateChanged(e); + } + } + } + + + /** + * 注册状态改变监听 + * + * @param listener 观察者监听事件 + */ + public void registerChangeListener(UIObserverListener listener) { + uiObserverListener = listener; + } + + /** + * 是否需要响应监听 + * + * @return 是否响应 + */ + public boolean shouldResponseChangeListener() { + return true; + } + + /** + * 注册监听 + * + * @param listener 观察者监听事件 + */ + public void registerNameListener(GlobalNameListener listener) { + globalNameListener = listener; + } + + /** + * 是否需要相应 + * + * @return 是否响应 + */ + public boolean shouldResponseNameListener() { + return true; + } +} diff --git a/src/main/java/com/fanruan/api/design/ui/component/UIToggleButton.java b/src/main/java/com/fanruan/api/design/ui/component/UIToggleButton.java new file mode 100644 index 0000000..7c12824 --- /dev/null +++ b/src/main/java/com/fanruan/api/design/ui/component/UIToggleButton.java @@ -0,0 +1,245 @@ +package com.fanruan.api.design.ui.component; + +import com.fanruan.api.design.macro.UIConstants; +import com.fanruan.api.util.StringKit; +import com.fr.design.event.GlobalNameListener; +import com.fr.design.event.GlobalNameObserver; +import com.fr.design.gui.ibutton.UIButtonUI; +import com.fr.design.roleAuthority.ReportAndFSManagePane; +import com.fr.design.utils.gui.GUIPaintUtils; + +import javax.swing.*; +import javax.swing.event.ChangeEvent; +import javax.swing.event.ChangeListener; +import java.awt.*; +import java.awt.event.ActionEvent; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.awt.event.MouseListener; +import java.awt.geom.RoundRectangle2D; + +/** + * @author richie + * @version 10.0 + * Created by richie on 2019/10/25 + * 可切换状态的按钮 + */ +public class UIToggleButton extends UIButton implements GlobalNameObserver { + + private static final int ICON_COUNT = 2; + private boolean selected; + private boolean eventBanned = false; + private String toggleButtonName = StringKit.EMPTY; + private GlobalNameListener globalNameListener = null; + private Icon[] icons; + + public UIToggleButton() { + this(StringKit.EMPTY); + } + + public UIToggleButton(Icon image) { + this(StringKit.EMPTY, image); + } + + public UIToggleButton(String text) { + this(text, null); + } + + public UIToggleButton(String text, Icon image) { + super(text, image); + addMouseListener(getMouseListener()); + } + + /** + * 需要反白的按钮接口(组合按钮情况-UIButtonGroup) + * support icons[normalIcon, selectedIcon] + * + * @param icons 图标 + */ + public UIToggleButton(Icon[] icons) { + super(icons[0], null, null); + setExtraPainted(true); + this.icons = icons; + addActionListener(new AbstractAction() { + @Override + public void actionPerformed(ActionEvent e) { + if (!UIToggleButton.super.isSelected()) { + UIToggleButton.super.setSelected(!UIToggleButton.super.isSelected()); + } + } + }); + addMouseListener(getMouseListener()); + } + + /** + * 需要反白的按钮接口(单个按钮情况)-再次点击取消选中状态 + * + * @param icons 图标 + * @param needRelease 是否能够点击之后释放 + */ + public UIToggleButton(Icon[] icons, boolean needRelease) { + super(icons[0], null, null); + setBorderPainted(true); + setExtraPainted(true); + this.icons = icons; + if (!needRelease) { + addActionListener(new AbstractAction() { + @Override + public void actionPerformed(ActionEvent e) { + if (UIToggleButton.super.isSelected()) { + UIToggleButton.super.setSelected(false); + } else { + UIToggleButton.super.setSelected(true); + } + } + }); + } + addMouseListener(getMouseListener()); + } + + @Override + public final void setGlobalName(String name) { + toggleButtonName = name; + } + + @Override + public final boolean isSelected() { + return selected; + } + + @Override + public final void setSelected(boolean isSelected) { + super.setSelected(isSelected); + if (this.selected != isSelected) { + this.selected = isSelected; + refresh(isSelected); + } + } + + @Override + protected void initListener() { + if (shouldResponseChangeListener()) { + this.addChangeListener(new ChangeListener() { + @Override + public void stateChanged(ChangeEvent e) { + if (uiObserverListener == null) { + return; + } + if (globalNameListener != null && shouldResponseNameListener()) { + globalNameListener.setGlobalName(toggleButtonName); + } + uiObserverListener.doChange(); + } + }); + } + } + + public final void setSelectedWithFireListener(boolean isSelected) { + if (this.selected != isSelected) { + this.selected = isSelected; + fireSelectedChanged(); + refresh(isSelected); + } + } + + + private void refresh(final boolean isSelected) { + SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + Icon[] icons = UIToggleButton.this.icons; + if (icons != null && icons.length == ICON_COUNT) { + if (isSelected) { + UIToggleButton.this.setIcon(icons[1]); + } else { + UIToggleButton.this.setIcon(icons[0]); + } + } + UIToggleButton.this.repaint(); + } + }); + } + + + protected MouseListener getMouseListener() { + return new MouseAdapter() { + @Override + public void mouseClicked(MouseEvent e) { + if (isEnabled() && !eventBanned) { + setSelectedWithFireListener(!isSelected()); + } + } + }; + } + + public void setEventBanned(boolean ban) { + this.eventBanned = ban; + } + + @Override + protected void fireStateChanged() { + + } + + + protected final void fireSelectedChanged() { + Object[] listeners = listenerList.getListenerList(); + + for (int i = listeners.length - 2; i >= 0; i -= 2) { + if (listeners[i] == ChangeListener.class) { + ((ChangeListener) listeners[i + 1]).stateChanged(new ChangeEvent(this)); + } + } + } + + @Override + protected final void paintBorder(Graphics g) { + if (!isBorderPainted()) { + return; + } + boolean isBorderPainted = isBorderPaintedOnlyWhenPressed && (getModel().isPressed() || selected); + if (isBorderPainted || !isBorderPaintedOnlyWhenPressed) { + if (ui instanceof UIButtonUI) { + drawRoleName((Graphics2D) g); + } else { + super.paintBorder(g); + } + } + } + + private void drawRoleName(Graphics2D g) { + String roleName = ReportAndFSManagePane.getInstance().getRoleTree().getSelectedRoleName(); + GUIPaintUtils.drawBorder(g, 0, 0, getWidth(), getHeight(), isRoundBorder(), getRectDirection(), isDoneAuthorityEdited(roleName)); + } + + @Override + protected final void paintOtherBorder(Graphics g) { + Graphics2D g2d = (Graphics2D) g; + g2d.setStroke(UIConstants.BS); + Shape shape = new RoundRectangle2D.Float(0.5f, 0.5f, getWidth() - 1F, getHeight() - 1F, UIConstants.ARC, UIConstants.ARC); + g2d.setColor(UIConstants.LINE_COLOR); + g2d.draw(shape); + } + + /** + * 组件是否需要响应添加的观察者事件 + * + * @return 如果需要响应观察者事件则返回true,否则返回false + */ + @Override + public final boolean shouldResponseChangeListener() { + return true; + } + + /** + * @param listener 观察者监听事件 + */ + @Override + public final void registerNameListener(GlobalNameListener listener) { + globalNameListener = listener; + } + + public final boolean shouldResponseNameListener() { + return true; + } +} diff --git a/src/main/java/com/fanruan/api/design/ui/component/pane/UITextPane.java b/src/main/java/com/fanruan/api/design/ui/component/pane/UITextPane.java new file mode 100644 index 0000000..d30b5e3 --- /dev/null +++ b/src/main/java/com/fanruan/api/design/ui/component/pane/UITextPane.java @@ -0,0 +1,29 @@ +package com.fanruan.api.design.ui.component.pane; + +import javax.swing.text.StyledDocument; + +/** + * @author richie + * @version 10.0 + * Created by richie on 2019/10/25 + * 用于显示文本的容器(可显示富文本) + * @see javax.swing.JTextPane + */ +public class UITextPane extends com.fr.design.gui.frpane.UITextPane { + + /** + * 构造容器 + */ + public UITextPane() { + super(); + } + + /** + * 根据特定的样式构造容器 + * + * @param document 样式 + */ + public UITextPane(StyledDocument document) { + super(document); + } +} diff --git a/src/main/java/com/fanruan/api/design/work/RichTextEditingPane.java b/src/main/java/com/fanruan/api/design/work/RichTextEditingPane.java new file mode 100644 index 0000000..5d44295 --- /dev/null +++ b/src/main/java/com/fanruan/api/design/work/RichTextEditingPane.java @@ -0,0 +1,188 @@ +package com.fanruan.api.design.work; + +import com.fanruan.api.design.DesignKit; +import com.fanruan.api.design.ui.component.pane.UITextPane; +import com.fanruan.api.log.LogKit; +import com.fanruan.api.util.AssistKit; +import com.fr.base.BaseFormula; +import com.fr.design.dialog.DialogActionAdapter; +import com.fr.design.formula.FormulaFactory; +import com.fr.design.formula.UIFormula; +import com.fr.report.cell.cellattr.core.RichText; +import com.fr.report.cell.cellattr.core.RichTextConverter; + +import javax.swing.text.AttributeSet; +import javax.swing.text.BadLocationException; +import javax.swing.text.Element; +import javax.swing.text.StyledDocument; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.awt.event.MouseListener; + +/** + * @author richie + * @version 10.0 + * Created by richie on 2019/10/25 + * 富文本编辑器容器 + */ +public class RichTextEditingPane extends UITextPane { + + private static final int WRAPPER_LEN = 3; + + private static final int PREFIX_LEN = 2; + + private boolean updating = false; + + /** + * 构造函数 + */ + public RichTextEditingPane() { + //双击选取公式监听器 + //往前回溯, 寻找${, 如果发现先找到了}, 说明不在公式内部, 直接return. + //有可能当前字符刚好处于{后面, 所以要-1 + //发现大括号了, 再找$ + //再往后找"}" + //发现左大括号了, 肯定异常 + //要把后缀包进去, 所以+1 + // 取得选择文本的起始位置和结束位置 + //公式起点 + //公式终点 + //找到公式的起点与终点了, 下面就是选中, 并弹出编辑窗口 + //缓存第一个字符的样式, 用于给新公式设置样式 + //弹出公式编辑窗口 + MouseListener doubleClickFormulaListener = new MouseAdapter() { + + private int findFormulaStart(int start, StyledDocument doc) throws BadLocationException { + //往前回溯, 寻找${, 如果发现先找到了}, 说明不在公式内部, 直接return. + //有可能当前字符刚好处于{后面, 所以要-1 + for (int i = start - 1; i >= 0; i--) { + String text = doc.getText(i, 1); + if (AssistKit.equals(text, RichText.SUFFIX)) { + return -1; + } + + //发现大括号了, 再找$ + if (AssistKit.equals(text, RichText.PREFIX)) { + if (i - 1 >= 0 && AssistKit.equals(doc.getText(i - 1, 1), RichText.FLAG)) { + return i - 1; + } + } + } + + return -1; + } + + private int findFormulaEnd(int start, StyledDocument doc) throws BadLocationException { + //再往后找"}" + int total = doc.getLength(); + for (int j = start; j < total; j++) { + String text = doc.getText(j, 1); + //发现左大括号了, 肯定异常 + if (AssistKit.equals(text, RichText.PREFIX)) { + return -1; + } + + if (AssistKit.equals(text, RichText.SUFFIX)) { + //要把后缀包进去, 所以+1 + return j + 1; + } + } + + return -1; + } + + private void popUpFormulaEditPane(final String formulaContent, final int formulaStart, + final AttributeSet attrs) { + final UIFormula formulaPane = FormulaFactory.createFormulaPane(); + formulaPane.populate(BaseFormula.createFormulaBuilder().build(formulaContent)); + formulaPane.showLargeWindow(DesignKit.getDesignerFrame(), new DialogActionAdapter() { + @Override + public void doOk() { + StyledDocument doc = (StyledDocument) RichTextEditingPane.this.getDocument(); + BaseFormula fm = formulaPane.update(); + String content = RichTextConverter.asFormula(fm.getContent()); + try { + doc.remove(formulaStart, formulaContent.length() + WRAPPER_LEN); + doc.insertString(formulaStart, content, attrs); + } catch (BadLocationException e) { + LogKit.error(e.getMessage(), e); + } + } + }).setVisible(true); + } + + @Override + public void mouseReleased(MouseEvent e) { + if (e.getClickCount() == 2) { + // 取得选择文本的起始位置和结束位置 + int start = RichTextEditingPane.this.getSelectionStart(); + + if (start <= 0) { + return; + } + + StyledDocument doc = (StyledDocument) RichTextEditingPane.this.getDocument(); + try { + //公式起点 + final int formulaStart = findFormulaStart(start, doc); + if (formulaStart == -1) { + return; + } + + //公式终点 + int formulaEnd = findFormulaEnd(start, doc); + if (formulaEnd == -1) { + return; + } + + //找到公式的起点与终点了, 下面就是选中, 并弹出编辑窗口 + RichTextEditingPane.this.select(formulaStart, formulaEnd); + //缓存第一个字符的样式, 用于给新公式设置样式 + Element ele = doc.getCharacterElement(formulaStart); + final AttributeSet attrs = ele.getAttributes(); + + final String formulaContent = doc.getText(formulaStart + PREFIX_LEN, formulaEnd - formulaStart - WRAPPER_LEN); + //弹出公式编辑窗口 + popUpFormulaEditPane(formulaContent, formulaStart, attrs); + } catch (BadLocationException e1) { + LogKit.error(e1.getMessage()); + } + } + } + }; + this.addMouseListener(doubleClickFormulaListener); + } + + /** + * 是否有其他进程正在更新编辑区域 + * + * @return 是否有其他进程正在更新编辑区域 + */ + public boolean isUpdating() { + return updating; + } + + /** + * 设置更新状态 + * + * @param updating 是否正在更新 + */ + public void setUpdating(boolean updating) { + this.updating = updating; + } + + /** + * 开始更新 + */ + public void startUpdating() { + this.updating = true; + } + + /** + * 结束更新 + */ + public void finishUpdating() { + this.updating = false; + } + +} diff --git a/src/main/java/com/fanruan/api/design/work/RichTextToolBar.java b/src/main/java/com/fanruan/api/design/work/RichTextToolBar.java new file mode 100644 index 0000000..4fa9425 --- /dev/null +++ b/src/main/java/com/fanruan/api/design/work/RichTextToolBar.java @@ -0,0 +1,612 @@ +package com.fanruan.api.design.work; + +import com.fanruan.api.design.DesignKit; +import com.fanruan.api.design.ui.component.UIButton; +import com.fanruan.api.design.ui.component.UIComboBox; +import com.fanruan.api.design.ui.component.UIToggleButton; +import com.fanruan.api.log.LogKit; +import com.fanruan.api.util.GeneralKit; +import com.fanruan.api.util.IOKit; +import com.fr.base.BaseFormula; +import com.fr.design.dialog.BasicPane; +import com.fr.design.dialog.DialogActionAdapter; +import com.fr.design.formula.FormulaFactory; +import com.fr.design.formula.UIFormula; +import com.fr.design.gui.style.FRFontPane; +import com.fr.design.mainframe.DesignerContext; +import com.fr.design.report.RichTextEditingPane; +import com.fr.design.report.RichTextPane; +import com.fr.design.style.color.UIToolbarColorButton; +import com.fr.general.FRFont; +import com.fr.report.cell.cellattr.core.RichTextConverter; +import com.fr.stable.Constants; + +import javax.swing.*; +import javax.swing.event.CaretEvent; +import javax.swing.event.CaretListener; +import javax.swing.event.ChangeEvent; +import javax.swing.event.ChangeListener; +import javax.swing.event.DocumentEvent; +import javax.swing.event.DocumentListener; +import javax.swing.text.AttributeSet; +import javax.swing.text.BadLocationException; +import javax.swing.text.Element; +import javax.swing.text.MutableAttributeSet; +import javax.swing.text.SimpleAttributeSet; +import javax.swing.text.StyleConstants; +import javax.swing.text.StyledDocument; +import java.awt.*; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.ItemEvent; +import java.awt.event.ItemListener; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.awt.event.MouseListener; +import java.math.BigDecimal; + +/** + * @author richie + * @version 10.0 + * Created by richie on 2019/10/25 + * 富文本编辑器工具栏 + */ +public class RichTextToolBar extends BasicPane { + + private static final Dimension BUTTON_SIZE = new Dimension(24, 20); + + private static final int NOT_INITIALIZED = -1; + private static final int UPDATING = -2; + + private int inputStart = NOT_INITIALIZED; + + private UIComboBox fontNameComboBox; + private UIComboBox fontSizeComboBox; + private UIToggleButton bold; + private UIToggleButton italic; + private UIToggleButton underline; + + private UIToolbarColorButton colorSelectPane; + private UIToggleButton superPane; + private UIToggleButton subPane; + private UIToggleButton formulaPane; + + //外部传进来的 + private RichTextEditingPane textPane; + + public RichTextToolBar() { + this.initComponents(); + } + + public RichTextToolBar(RichTextEditingPane textPane) { + this.textPane = textPane; + + this.initComponents(); + } + + @Override + protected String title4PopupWindow() { + return DesignKit.i18nText("Fine-Design_Form_Font"); + } + + protected void initComponents() { + //初始化并设置所有按钮样式 + initAllButton(); + //添加到工具栏 + addToToolBar(); + } + + private void initAllButton() { + fontNameComboBox = new UIComboBox<>(GeneralKit.getAvailableFontFamilyNames()); + fontNameComboBox.setPreferredSize(new Dimension(144, 20)); + fontSizeComboBox = new UIComboBox<>(FRFontPane.getFontSizes()); + colorSelectPane = new UIToolbarColorButton(IOKit.readIcon("/com/fr/design/images/gui/color/foreground.png")); + colorSelectPane.set4Toolbar(); + + bold = new UIToggleButton(IOKit.readIcon("/com/fr/design/images/m_format/cellstyle/bold.png")); + italic = new UIToggleButton(IOKit.readIcon("/com/fr/design/images/m_format/cellstyle/italic.png")); + underline = new UIToggleButton(IOKit.readIcon("/com/fr/design/images/m_format/cellstyle/underline.png")); + superPane = new UIToggleButton(IOKit.readIcon("/com/fr/design/images/m_format/cellstyle/sup.png")); + subPane = new UIToggleButton(IOKit.readIcon("/com/fr/design/images/m_format/cellstyle/sub.png")); + formulaPane = new UIToggleButton(IOKit.readIcon("/com/fr/design/images/m_insert/formula.png")); + + //名字 + initAllNames(); + //悬浮提示 + setToolTips(); + //样式 + setAllButtonStyle(); + //绑定监听器 + bindListener(); + } + + private void setAllButtonStyle() { + setButtonStyle(bold); + setButtonStyle(italic); + setButtonStyle(underline); + setButtonStyle(subPane); + setButtonStyle(superPane); + setButtonStyle(formulaPane); + } + + private void setButtonStyle(UIButton button) { + button.setNormalPainted(false); + button.setBackground(null); + button.setOpaque(false); + button.setPreferredSize(BUTTON_SIZE); + button.setBorderPaintedOnlyWhenPressed(true); + } + + private void addToToolBar() { + this.setLayout(new FlowLayout(FlowLayout.LEFT)); + + this.add(fontNameComboBox); + this.add(fontSizeComboBox); + this.add(bold); + this.add(italic); + this.add(underline); + this.add(colorSelectPane); + this.add(superPane); + this.add(subPane); + this.add(formulaPane); + } + + private void bindListener() { + FRFont defaultFont = (this.textPane != null) ? FRFont.getInstance(this.textPane.getFont()) : RichTextPane.DEFAUL_FONT; + fontNameComboBox.addItemListener(fontNameItemListener); + fontNameComboBox.setSelectedItem(defaultFont.getFontName()); + fontSizeComboBox.addItemListener(fontSizeItemListener); + fontSizeComboBox.setSelectedItem(scaleDown(defaultFont.getSize())); + + bold.addActionListener(blodChangeAction); + italic.addActionListener(itaChangeAction); + underline.addActionListener(underlineChangeAction); + subPane.addActionListener(subChangeAction); + superPane.addActionListener(superChangeAction); + colorSelectPane.addColorChangeListener(colorChangeAction); + formulaPane.addActionListener(formulaActionListener); + + //选中文字的监听器 + textPane.addCaretListener(textCareListener); + textPane.addMouseListener(setMouseCurrentStyle); + textPane.getDocument().addDocumentListener(inputListener); + } + + private void initAllNames() { + fontNameComboBox.setGlobalName(DesignKit.i18nText("Fine-Design_Report_Font_Family")); + fontSizeComboBox.setGlobalName(DesignKit.i18nText("Fine-Design_Form_Font_Size")); + italic.setGlobalName(DesignKit.i18nText("Fine-Design_Report_Italic")); + bold.setGlobalName(DesignKit.i18nText("Fine-Design_Report_Bold")); + underline.setGlobalName(DesignKit.i18nText("Fine-Design_Report_Underline")); + superPane.setGlobalName(DesignKit.i18nText("Fine-Design_Report_Super_Script")); + subPane.setGlobalName(DesignKit.i18nText("Fine-Design_Report_Sub_Script")); + } + + private void setToolTips() { + colorSelectPane.setToolTipText(DesignKit.i18nText("Fine-Design_Report_Foreground")); + italic.setToolTipText(DesignKit.i18nText("Fine-Design_Report_Italic")); + bold.setToolTipText(DesignKit.i18nText("Fine-Design_Report_Bold")); + underline.setToolTipText(DesignKit.i18nText("Fine-Design_Report_Underline")); + superPane.setToolTipText(DesignKit.i18nText("Fine-Design_Report_Super_Script")); + subPane.setToolTipText(DesignKit.i18nText("Fine-Design_Report_Sub_Script")); + formulaPane.setToolTipText(DesignKit.i18nText("Fine-Design_Basic_Formula")); + } + + /** + * 移除输入监听 + * 用于populate时, 插入字符串, 那时不需要插入监听 + */ + public void removeInputListener() { + this.textPane.getDocument().removeDocumentListener(inputListener); + } + + /** + * 增加输入监听事件 + */ + public void addInputListener() { + this.textPane.getDocument().addDocumentListener(inputListener); + } + + private ActionListener blodChangeAction = new ActionListener() { + + @Override + public void actionPerformed(ActionEvent e) { + boolean isBold = RichTextToolBar.this.bold.isSelected(); + // 调用setCharacterAttributes函数设置文本区选择文本的字体 + MutableAttributeSet attr = new SimpleAttributeSet(); + StyleConstants.setBold(attr, !isBold); + setCharacterAttributes(RichTextToolBar.this.textPane, attr, false); + } + }; + + private ActionListener itaChangeAction = new ActionListener() { + + @Override + public void actionPerformed(ActionEvent e) { + boolean isIta = RichTextToolBar.this.italic.isSelected(); + // 调用setCharacterAttributes函数设置文本区选择文本的字体 + MutableAttributeSet attr = new SimpleAttributeSet(); + StyleConstants.setItalic(attr, !isIta); + setCharacterAttributes(RichTextToolBar.this.textPane, attr, false); + } + }; + + private ActionListener underlineChangeAction = new ActionListener() { + + @Override + public void actionPerformed(ActionEvent e) { + boolean isUnder = RichTextToolBar.this.underline.isSelected(); + // 调用setCharacterAttributes函数设置文本区选择文本的字体 + MutableAttributeSet attr = new SimpleAttributeSet(); + StyleConstants.setUnderline(attr, !isUnder); + setCharacterAttributes(RichTextToolBar.this.textPane, attr, false); + } + }; + private ActionListener subChangeAction = new ActionListener() { + + @Override + public void actionPerformed(ActionEvent e) { + boolean isSub = RichTextToolBar.this.subPane.isSelected(); + // 调用setCharacterAttributes函数设置文本区选择文本的字体 + MutableAttributeSet attr = new SimpleAttributeSet(); + StyleConstants.setSubscript(attr, !isSub); + setCharacterAttributes(RichTextToolBar.this.textPane, attr, false); + } + }; + private ActionListener superChangeAction = new ActionListener() { + + @Override + public void actionPerformed(ActionEvent e) { + boolean isSuper = RichTextToolBar.this.superPane.isSelected(); + // 调用setCharacterAttributes函数设置文本区选择文本的字体 + MutableAttributeSet attr = new SimpleAttributeSet(); + StyleConstants.setSuperscript(attr, !isSuper); + setCharacterAttributes(RichTextToolBar.this.textPane, attr, false); + } + }; + + private ChangeListener colorChangeAction = new ChangeListener() { + @Override + public void stateChanged(ChangeEvent e) { + Color color = RichTextToolBar.this.colorSelectPane.getColor(); + color = color == null ? Color.BLACK : color; + // 调用setCharacterAttributes函数设置文本区选择文本的字体 + MutableAttributeSet attr = new SimpleAttributeSet(); + StyleConstants.setForeground(attr, color); + setCharacterAttributes(RichTextToolBar.this.textPane, attr, false); + } + }; + + // 设置文本区选择文本的样式 + private void setCharacterAttributes(JEditorPane editor, AttributeSet attr, + boolean replace) { + //注意不要失焦 + textPane.requestFocus(); + + // 取得选择文本的起始位置和结束位置 + int start = editor.getSelectionStart(); + int end = editor.getSelectionEnd(); + + // 如果选中文本,设置选中文本的样式 + if (start != end) { + StyledDocument doc = (StyledDocument) textPane.getDocument(); + // 将所选文本设置为新的样式,replace为false表示不覆盖原有的样式 + doc.setCharacterAttributes(start, end - start, attr, replace); + } + } + + private ItemListener fontSizeItemListener = new ItemListener() { + @Override + public void itemStateChanged(ItemEvent e) { + int fontSize = (Integer) RichTextToolBar.this.fontSizeComboBox.getSelectedItem(); + fontSize = scaleUp(fontSize); + // 调用setCharacterAttributes函数设置文本区选择文本的字体 + MutableAttributeSet attr = new SimpleAttributeSet(); + StyleConstants.setFontSize(attr, fontSize); + setCharacterAttributes(RichTextToolBar.this.textPane, attr, false); + } + }; + + private ItemListener fontNameItemListener = new ItemListener() { + @Override + public void itemStateChanged(ItemEvent e) { + String fontName = (String) RichTextToolBar.this.fontNameComboBox.getSelectedItem(); + // 调用setCharacterAttributes函数设置文本区选择文本的字体 + MutableAttributeSet attr = new SimpleAttributeSet(); + StyleConstants.setFontFamily(attr, fontName); + setCharacterAttributes(RichTextToolBar.this.textPane, attr, false); + } + }; + + private ActionListener formulaActionListener = new ActionListener() { + public void actionPerformed(ActionEvent evt) { + final UIFormula formulaPane = FormulaFactory.createFormulaPane(); + formulaPane.populate(BaseFormula.createFormulaBuilder().build()); + formulaPane.showLargeWindow(DesignerContext.getDesignerFrame(), new DialogActionAdapter() { + @Override + public void doOk() { + StyledDocument doc = (StyledDocument) textPane.getDocument(); + BaseFormula fm = formulaPane.update(); + String content = RichTextConverter.asFormula(fm.getContent()); + int start = textPane.getSelectionStart(); + AttributeSet attrs = start > 0 ? doc.getCharacterElement(start - 1).getAttributes() : new SimpleAttributeSet(); + try { + doc.insertString(start, content, attrs); + } catch (BadLocationException e) { + LogKit.error(e.getMessage(), e); + } + } + }).setVisible(true); + } + }; + + private int roundUp(double num) { + String numStr = Double.toString(num); + numStr = new BigDecimal(numStr).setScale(0, BigDecimal.ROUND_HALF_UP).toString(); + return Integer.parseInt(numStr); + } + + private CaretListener textCareListener = new CaretListener() { + + //根据选中部分的文字样式, 来动态显示工具栏上按钮的状态 + private void setSelectedCharStyle(int start, int end, StyledDocument doc) { + boolean isBold = true; + boolean isItalic = true; + boolean isUnderline = true; + boolean isSubscript = true; + boolean isSuperscript = true; + String fontName_1st = null; + int fontSize_1st = 0; + Color fontColor_1st = null; + + for (int i = start; i < end; i++) { + Element ele = doc.getCharacterElement(i); + AttributeSet attrs = ele.getAttributes(); + + //粗体 + isBold = isBold && StyleConstants.isBold(attrs); + //斜体 + isItalic = isItalic && StyleConstants.isItalic(attrs); + //下划线 + isUnderline = isUnderline && StyleConstants.isUnderline(attrs); + //下标 + isSubscript = isSubscript && StyleConstants.isSubscript(attrs); + //上标 + isSuperscript = isSuperscript && StyleConstants.isSuperscript(attrs); + + if (i == start) { + fontName_1st = (String) attrs.getAttribute(StyleConstants.FontFamily); + fontSize_1st = (Integer) attrs.getAttribute(StyleConstants.FontSize); + fontColor_1st = (Color) attrs.getAttribute(StyleConstants.Foreground); + fontColor_1st = fontColor_1st == null ? Color.BLACK : fontColor_1st; + } + } + + setButtonSelected(isBold, isItalic, isUnderline, isSubscript, isSuperscript, + fontName_1st, fontSize_1st, fontColor_1st); + } + + //动态显示工具栏上按钮的状态 + private void setButtonSelected(boolean isBold, boolean isItalic, boolean isUnderline, + boolean isSubscript, boolean isSuperscript, String fontName_1st, + int fontSize_1st, Color fontColor_1st) { + bold.setSelected(isBold); + italic.setSelected(isItalic); + underline.setSelected(isUnderline); + subPane.setSelected(isSubscript); + superPane.setSelected(isSuperscript); + //为什么字体名称, 大小, 颜色, 不需要去判断是否全相同呢 + //因为如果全相同, 则设置为第一个字符的样式, 如果不全相同, 那么默认也设置成第一个字符的样式. + fontNameComboBox.setSelectedItem(fontName_1st); + fontSizeComboBox.removeItemListener(fontSizeItemListener); + fontSizeComboBox.setSelectedItem(scaleDown(fontSize_1st)); + fontSizeComboBox.addItemListener(fontSizeItemListener); + selectColorPane(fontColor_1st); + } + + private void selectColorPane(Color color) { + colorSelectPane.removeColorChangeListener(colorChangeAction); + colorSelectPane.setColor(color); + colorSelectPane.addColorChangeListener(colorChangeAction); + } + + @Override + public void caretUpdate(CaretEvent e) { + StyledDocument doc = (StyledDocument) textPane.getDocument(); + + // 取得选择文本的起始位置和结束位置 + int start = textPane.getSelectionStart(); + int end = textPane.getSelectionEnd(); + + //如果没有选定字符 + if (end == start) { + return; + } + + setSelectedCharStyle(start, end, doc); + } + }; + + //设置当前光标位样式 + private MouseListener setMouseCurrentStyle = new MouseAdapter() { + + @Override + public void mouseClicked(MouseEvent e) { + StyledDocument doc = (StyledDocument) textPane.getDocument(); + + // 取得选择文本的起始位置和结束位置 + int start = textPane.getSelectionStart(); + int end = textPane.getSelectionEnd(); + + if (start != end) { + return; + } + + setToLastCharStyle(end, doc); + } + + //如果默认不选字符, 那么设置为最后一个字符的样式 + private void setToLastCharStyle(int end, StyledDocument doc) { + if (textPane.isUpdating()) { + return; + } + + //取前一个字符的样式 + Element ele = doc.getCharacterElement(end - 1); + AttributeSet attrs = ele.getAttributes(); + populateToolBar(attrs); + } + }; + + /** + * 从样式中更新工具栏上的按钮状态 + * + * @param attrs 样式 + * @date 2015-1-5-下午5:12:33 + */ + public void populateToolBar(AttributeSet attrs) { + int size = scaleDown(StyleConstants.getFontSize(attrs)); + fontNameComboBox.setSelectedItem(StyleConstants.getFontFamily(attrs)); + fontSizeComboBox.setSelectedItem(size); + + bold.setSelected(StyleConstants.isBold(attrs)); + italic.setSelected(StyleConstants.isItalic(attrs)); + underline.setSelected(StyleConstants.isUnderline(attrs)); + subPane.setSelected(StyleConstants.isSubscript(attrs)); + superPane.setSelected(StyleConstants.isSuperscript(attrs)); + Color foreGround = StyleConstants.getForeground(attrs); + foreGround = foreGround == null ? Color.BLACK : foreGround; + colorSelectPane.setColor(foreGround); + colorSelectPane.repaint(); + } + + //pt转为px =*4/3 + private int scaleUp(int fontSize) { + return scale(fontSize, true); + } + + //px转pt = *3/4 + private int scaleDown(int fontSize) { + return scale(fontSize, false); + } + + private int scale(int fontSize, boolean isUp) { + double dpi96 = Constants.FR_PAINT_RESOLUTION; + double dpi72 = Constants.DEFAULT_FONT_PAINT_RESOLUTION; + double scale = isUp ? (dpi96 / dpi72) : (dpi72 / dpi96); + + return roundUp(fontSize * scale); + } + + private DocumentListener inputListener = new DocumentListener() { + + @Override + public void removeUpdate(DocumentEvent e) { + } + + @Override + public void insertUpdate(DocumentEvent e) { + //标志正在更新内容 + textPane.startUpdating(); + final MutableAttributeSet attr = updateStyleFromToolBar(); + final int start = textPane.getSelectionStart(); + int end = textPane.getSelectionEnd(); + + if (start != end) { + textPane.finishUpdating(); + return; + } + + //放到SwingWorker里, 是因为在documentListener里不能动态改变doc内容 + SwingUtilities.invokeLater(new Runnable() { + + @Override + public void run() { + changeContentStyle(start, attr); + } + }); + } + + //根据Style来显示populate按钮 + private void changeContentStyle(int start, MutableAttributeSet attr) { + changeContentStyle(start, attr, 1); + } + + private void changeContentStyle(int start, MutableAttributeSet attr, int contentLength) { + // 将所选文本设置为新的样式,replace为false表示不覆盖原有的样式 + StyledDocument doc = (StyledDocument) textPane.getDocument(); + doc.setCharacterAttributes(start, contentLength, attr, false); + textPane.finishUpdating(); + } + + //将界面上的设置赋值给输入的字符 + private MutableAttributeSet updateStyleFromToolBar() { + final boolean isBold = bold.isSelected(); + final boolean isItalic = italic.isSelected(); + final boolean isSub = subPane.isSelected(); + final boolean isSuper = superPane.isSelected(); + final boolean isUnderLine = underline.isSelected(); + final String fontName = (String) fontNameComboBox.getSelectedItem(); + final int fontSize = scaleUp((Integer) fontSizeComboBox.getSelectedItem()); + final Color foreGround = colorSelectPane.getColor() == null ? Color.BLACK : colorSelectPane.getColor(); + + MutableAttributeSet attr = new SimpleAttributeSet(); + StyleConstants.setBold(attr, isBold); + StyleConstants.setItalic(attr, isItalic); + StyleConstants.setSubscript(attr, isSub); + StyleConstants.setSuperscript(attr, isSuper); + StyleConstants.setUnderline(attr, isUnderLine); + StyleConstants.setForeground(attr, foreGround); + StyleConstants.setFontFamily(attr, fontName); + StyleConstants.setFontSize(attr, fontSize); + + return attr; + } + + @Override + public void changedUpdate(DocumentEvent e) { + + } + + private void setContentStyle(final int inputLen) { + //缓存下Start, 下面要用来设置样式 + final int _start = inputStart; + final MutableAttributeSet attr = updateStyleFromToolBar(); + + //放到SwingWorker里, 是因为在documentListener里不能动态改变doc内容 + SwingUtilities.invokeLater(new Runnable() { + + @Override + public void run() { + //防止触发死循环change事件 + startUpdating(); + //Start-1 是因为中文输入法会用空格占1位 + changeContentStyle(_start, attr, inputLen); + resetFlag(); + } + }); + } + + private boolean isUpdating() { + return inputStart == UPDATING; + } + + private void startUpdating() { + inputStart = UPDATING; + } + + //初始标记状态, 用于记录中文输入法多个字符同时输入的问题 + private void initFlag(StyledDocument doc) { + if (inputStart != NOT_INITIALIZED) { + return; + } + inputStart = textPane.getSelectionStart() - 1; + } + + //重置标记状态 + private void resetFlag() { + inputStart = NOT_INITIALIZED; + } + }; + +} diff --git a/src/main/java/com/fanruan/api/design/work/component/LineComboBox.java b/src/main/java/com/fanruan/api/design/work/component/LineComboBox.java new file mode 100644 index 0000000..12b4f98 --- /dev/null +++ b/src/main/java/com/fanruan/api/design/work/component/LineComboBox.java @@ -0,0 +1,133 @@ +package com.fanruan.api.design.work.component; + +import com.fanruan.api.design.DesignKit; +import com.fanruan.api.design.ui.component.UIComboBox; +import com.fanruan.api.macro.LineConstants; +import com.fanruan.api.util.GraphKit; +import com.fr.base.FRContext; +import com.fr.base.ScreenResolution; +import com.fr.design.gui.icombobox.UIComboBoxRenderer; +import com.fr.general.FRFont; + +import javax.swing.*; +import java.awt.*; +import java.awt.geom.GeneralPath; +import java.awt.geom.Point2D; + +/** + * @author richie + * @version 10.0 + * Created by richie on 2019/10/25 + * 用于选择线条类型的下拉框 + */ +public class LineComboBox extends UIComboBox { + + public LineComboBox(int[] lineStyleArray) { + Integer[] lineStyleIntegerArray = new Integer[lineStyleArray.length]; + + for (int i = 0; i < lineStyleArray.length; i++) { + lineStyleIntegerArray[i] = lineStyleArray[i]; + } + + this.setModel(new DefaultComboBoxModel(lineStyleIntegerArray)); + + this.setRenderer(new LineComboBox.LineCellRenderer()); + } + + /** + * 获取选择的线条类型 + * + * @return 用于表示线条类型的整数 + */ + public int getSelectedLineStyle() { + int style = (Integer) getSelectedItem(); + return (style < 0) ? LineConstants.LINE_NONE : style; + } + + /** + * 设置选中的线条类型 + * + * @param style 用于表示线条类型的整数 + */ + public void setSelectedLineStyle(int style) { + this.setSelectedItem(style); + } + + private static class LineCellRenderer extends UIComboBoxRenderer { + + private int style = LineConstants.LINE_NONE; + + public Component getListCellRendererComponent( + JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) { + JLabel comp = (JLabel) super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus); + this.style = (Integer) value; + comp.setText(null); + return comp; + } + + public void paint(Graphics g) { + super.paint(g); + Graphics2D g2d = (Graphics2D) g; + + Dimension d = getSize(); + g2d.setColor(getForeground()); + + FRFont font = FRContext.getDefaultValues().getFRFont(); + int resolution = ScreenResolution.getScreenResolution(); + Font rfont = font.applyResolutionNP(resolution); + g2d.setFont(rfont); + FontMetrics fm = GraphKit.getFontMetrics(rfont); + if (style == LineConstants.LINE_NONE) { + GraphKit.drawString(g2d, DesignKit.i18nText("Fine-Design_Report_None"), 4, (d.height - fm.getHeight()) / 2D + fm.getAscent()); + } else { + GraphKit.drawLine(g2d, 4, d.height / 2D, d.width - 8D, d.height / 2D, style); + } + + if (isShowAxisWithLineStyle()) { // 带有坐标轴箭头的样式. + drawArrow(g2d, new Point2D.Double(4, d.height / 2D), new Point2D.Double(d.width - 8D, d.height / 2D)); + } + } + + private void drawArrow(Graphics2D g2d, Point2D p0, Point2D p1) { + Point2D s = new Point2D.Double(p1.getX() - p0.getX(), p1.getY() - p0.getY()); + Point2D t = new Point2D.Double(); + double d1 = p0.distance(p1); + //d2-d5设定箭头的大小,p1-p2为坐标轴的延长线,p2-p5-p3-p6为箭头4个点的具体位置 + double d2 = 9; + double d3 = 15; + double d4 = 7; + double d5 = 3; + t.setLocation(d2 * s.getX() / d1, d2 * s.getY() / d1); + Point2D p2 = new Point2D.Double(p1.getX() + t.getX(), p1.getY() + t.getY()); + t.setLocation(d3 * s.getX() / d1, d3 * s.getY() / d1); + Point2D p3 = new Point2D.Double(p1.getX() + t.getX(), p1.getY() + t.getY()); + t.setLocation(d4 * s.getX() / d1, d4 * s.getY() / d1); + Point2D p4 = new Point2D.Double(p1.getX() + t.getX(), p1.getY() + t.getY()); + Point2D p5 = new Point2D.Double(p4.getX() + s.getY() / d1 * d5, p4.getY() - s.getX() / d1 * d5); + Point2D p6 = new Point2D.Double(p4.getX() - s.getY() / d1 * d5, p4.getY() + s.getX() / d1 * d5); + + GeneralPath arrow = new GeneralPath(); + arrow.moveTo((float) p2.getX() - 10, (float) p2.getY()); + arrow.lineTo((float) p5.getX() - 10, (float) p5.getY()); + arrow.lineTo((float) p3.getX() - 10, (float) p3.getY()); + arrow.lineTo((float) p6.getX() - 10, (float) p6.getY()); + arrow.closePath(); + + g2d.draw(arrow); + g2d.fill(arrow); + } + + private boolean isShowAxisWithLineStyle() { + return style == LineConstants.LINE_CHART_MED_ARROW + || style == LineConstants.LINE_CHART_THICK_ARROW || style == LineConstants.LINE_CHART_THIN_ARROW; + } + + public Dimension getPreferredSize() { + return new Dimension(60, 16); + } + + public Dimension getMinimumSize() { + return getPreferredSize(); + } + } +} diff --git a/src/main/java/com/fanruan/api/macro/LineConstants.java b/src/main/java/com/fanruan/api/macro/LineConstants.java new file mode 100644 index 0000000..aaa634f --- /dev/null +++ b/src/main/java/com/fanruan/api/macro/LineConstants.java @@ -0,0 +1,32 @@ +package com.fanruan.api.macro; + +/** + * @author richie + * @version 10.0 + * Created by richie on 2019/10/25 + * 用于表示线条样式 + */ +public class LineConstants { + + public static final int LINE_NONE = 0; + public static final int LINE_THIN = 1; + public static final int LINE_MEDIUM = 2; + public static final int LINE_DASH = 3; + public static final int LINE_HAIR = 4; + public static final int LINE_THICK = 5; + public static final int LINE_DOUBLE = 6; + public static final int LINE_DOT = 7; + public static final int LINE_MEDIUM_DASH = 8; + public static final int LINE_DASH_DOT = 9; + public static final int LINE_MEDIUM_DASH_DOT = 10; + public static final int LINE_DASH_DOT_DOT = 11; + public static final int LINE_MEDIUM_DASH_DOT_DOT = 12; + public static final int LINE_SLANTED_DASH_DOT = 13; + public static final int LINE_HAIR2 = 14; + public static final int LINE_DOUBLE_DOT = 15; + public static final int LINE_LARGE = 16; + public static final int LINE_SLIM = 17; + public static final int LINE_CHART_THIN_ARROW = 21; + public static final int LINE_CHART_MED_ARROW = 22; + public static final int LINE_CHART_THICK_ARROW = 23; +} diff --git a/src/main/java/com/fanruan/api/report/form/category/BaseBorderStyleWidget.java b/src/main/java/com/fanruan/api/report/form/category/BaseBorderStyleWidget.java new file mode 100644 index 0000000..b363455 --- /dev/null +++ b/src/main/java/com/fanruan/api/report/form/category/BaseBorderStyleWidget.java @@ -0,0 +1,21 @@ +package com.fanruan.api.report.form.category; + +import com.fanruan.api.report.form.BaseWidget; +import com.fr.form.ui.LayoutBorderStyle; +import com.fr.form.ui.PaddingMargin; +import com.fr.form.ui.RichStyleWidgetProvider; +import com.fr.general.Background; +import com.fr.general.act.BorderPacker; + +/** + * @author richie + * @version 10.0 + * Created by richie on 2019/10/25 + * 带边框和样式设置的控件基类 + */ +public abstract class BaseBorderStyleWidget extends BaseWidget implements RichStyleWidgetProvider { + + private BorderPacker borderStyle = new LayoutBorderStyle(); + private Background background; + private PaddingMargin margin = new PaddingMargin(); +} diff --git a/src/main/java/com/fanruan/api/util/GeneralKit.java b/src/main/java/com/fanruan/api/util/GeneralKit.java index 77d75f3..e645726 100644 --- a/src/main/java/com/fanruan/api/util/GeneralKit.java +++ b/src/main/java/com/fanruan/api/util/GeneralKit.java @@ -1,6 +1,7 @@ package com.fanruan.api.util; import com.fr.base.FRContext; +import com.fr.base.Utils; import com.fr.general.ComparatorUtils; import com.fr.general.GeneralUtils; import com.fr.stable.StableUtils; @@ -118,4 +119,33 @@ public class GeneralKit { return StableUtils.pathJoin(nodes); } + /** + * 获取系统可用用于报表设计的字体 + * + * @return 可用字体数组 + */ + public static String[] getAvailableFontFamilyNames() { + return Utils.getAvailableFontFamilyNames4Report(); + } + + /** + * 从rgb颜色中根据透明度过滤 + * + * @param rgb rgb颜色值 + * @param percent 透明度百分比 + * @return 新的颜色 + */ + public static int filterRGB(int rgb, int percent) { + int gray = (int) ((0.30 * ((rgb >> 16) & 0xff) + 0.59 * ((rgb >> 8) & 0xff) + 0.11 * (rgb & 0xff)) / 3); + gray = (255 - ((255 - gray) * (100 - percent) / 100)); + + if (gray < 0) { + gray = 0; + } + if (gray > 255) { + gray = 255; + } + return (rgb & 0xff000000) | (gray << 16) | (gray << 8) | (gray); + } + } diff --git a/src/main/java/com/fanruan/api/util/GraphKit.java b/src/main/java/com/fanruan/api/util/GraphKit.java new file mode 100644 index 0000000..a906e2e --- /dev/null +++ b/src/main/java/com/fanruan/api/util/GraphKit.java @@ -0,0 +1,104 @@ +package com.fanruan.api.util; + +import com.fr.stable.GraphDrawHelper; + +import java.awt.*; + +/** + * @author richie + * @version 10.0 + * Created by richie on 2019/10/25 + * 图形绘制相关的工具类 + */ +public class GraphKit { + + /** + * 获取字体规格属性 + * + * @param font 字体 + * @return 字体的规格属性 + */ + public static FontMetrics getFontMetrics(Font font) { + return GraphDrawHelper.getFontMetrics(font); + } + + /** + * 获取字体规格属性 + * + * @param font 字体 + * @param g2d 图形绘制上下文 + * @return 字体的规格属性 + */ + public static FontMetrics getFontMetrics(Font font, Graphics2D g2d) { + return GraphDrawHelper.getFontMetrics(font, g2d); + } + + /** + * 绘制文本 + * + * @param g 图形绘制上下文 + * @param str 待绘制的文本 + * @param x 横坐标 + * @param y 纵坐标 + */ + public static void drawString(Graphics g, String str, double x, double y) { + GraphDrawHelper.drawString(g, str, x, y); + } + + /** + * 绘制线段 + * + * @param g 图形绘制上下文 + * @param x1 起点横坐标 + * @param y1 起点纵坐标 + * @param x2 终点横坐标 + * @param y2 终点纵坐标 + */ + public static void drawLine(Graphics g, double x1, double y1, double x2, double y2) { + GraphDrawHelper.drawLine(g, x1, y1, x2, y2); + } + + /** + * 绘制线段 + * + * @param g 图形绘制上下文 + * @param x1 起点横坐标 + * @param y1 起点纵坐标 + * @param x2 终点横坐标 + * @param y2 终点纵坐标 + * @param lineStyle 线条样式 + * @see com.fanruan.api.macro.LineConstants + */ + public static void drawLine(Graphics g, + double x1, double y1, double x2, double y2, int lineStyle) { + GraphDrawHelper.drawLine(g, x1, y1, x2, y2, lineStyle); + } + + /** + * 绘制矩形 + * + * @param g 图形绘制上下文 + * @param x 起点横坐标 + * @param y 起点纵坐标 + * @param width 矩形宽度 + * @param height 矩形高度 + */ + public static void drawRect(Graphics g, double x, double y, double width, double height) { + GraphDrawHelper.drawRect(g, x, y, width, height); + } + + /** + * 绘制矩形 + * + * @param g 图形绘制上下文 + * @param x 起点横坐标 + * @param y 起点纵坐标 + * @param width 矩形宽度 + * @param height 矩形高度 + * @param lineStyle 线条样式 + * @see com.fanruan.api.macro.LineConstants + */ + public static void drawRect(Graphics g, double x, double y, double width, double height, int lineStyle) { + GraphDrawHelper.drawRect(g, x, y, width, height, lineStyle); + } +}