帆软报表设计器源代码。
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

528 lines
20 KiB

package com.fr.quickeditor;
import com.fr.base.GraphHelper;
import com.fr.base.Style;
import com.fr.design.actions.UpdateAction;
import com.fr.design.actions.core.ActionFactory;
import com.fr.design.file.HistoryTemplateListCache;
import com.fr.design.gui.frpane.AttributeChangeListener;
import com.fr.design.gui.icombobox.UIComboBox;
import com.fr.design.gui.ilable.UILabel;
import com.fr.design.gui.iscrollbar.UIScrollBar;
import com.fr.design.gui.itextfield.UITextField;
import com.fr.design.gui.style.TextFormatPaneContainer;
import com.fr.design.i18n.Toolkit;
import com.fr.design.layout.TableLayout;
import com.fr.design.layout.TableLayoutHelper;
import com.fr.design.mainframe.CellElementPropertyPane;
import com.fr.design.mainframe.DesignerContext;
import com.fr.design.mainframe.ElementCasePane;
import com.fr.design.mainframe.JTemplate;
import com.fr.design.menu.MenuKeySet;
import com.fr.design.menu.ShortCut;
import com.fr.design.selection.QuickEditor;
import com.fr.design.utils.gui.UIComponentUtils;
import com.fr.design.widget.FRWidgetFactory;
import com.fr.grid.selection.CellSelection;
import com.fr.quickeditor.cellquick.layout.CellElementBarLayout;
import com.fr.report.cell.TemplateCellElement;
import com.fr.report.elementcase.TemplateElementCase;
import com.fr.stable.ColumnRow;
import javax.swing.BorderFactory;
import javax.swing.JComponent;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollBar;
import javax.swing.SwingUtilities;
import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.AdjustmentEvent;
import java.awt.event.AdjustmentListener;
import java.awt.event.MouseWheelEvent;
import java.awt.event.MouseWheelListener;
import java.text.Format;
import java.util.ArrayList;
import java.util.Objects;
import java.util.Set;
/**
* @author zhou, yaoh.wu
* @version 2017年8月7日16点54分
* @since 1.0
*/
public abstract class CellQuickEditor extends QuickEditor<ElementCasePane> {
protected static final Dimension LABEL_DIMENSION = new Dimension(GraphHelper.getWidth(Toolkit.i18nText("Fine-Design_Report_Insert_Cell_Element")), 20);
// 使用UILabel.getPreferredSize时,若文本为html高度被默认增加3
protected static final Dimension HIDDEN_LABEL_DIMENSION = new Dimension(0, -3);
protected static final int VGAP = 10, HGAP = 8, VGAP_INNER = 3;
8 years ago
/**
* 滚动条相关配置
*/
private static final int MAXVALUE = 100;
private static final int CONTENT_PANE_WIDTH_GAP = 3;
private static final int MOUSE_WHEEL_SPEED = 5;
private static final int SCROLLBAR_WIDTH = 7;
private UILabel cellLabel;
private int maxHeight = 280;
private static final int TITLE_HEIGHT = 50;
/**
* 面板配置
*/
protected UITextField columnRowTextField;
protected TemplateCellElement cellElement;
private TextFormatPaneContainer formatPane;
private JPanel topContentContainer;
private JComponent centerBodyContainer;
private UILabel multipleLabelTip;
// 占位label
protected final UILabel EMPTY_LABEL = new UILabel();
private UIComboBox comboBox;
private UpdateAction[] cellInsertActions;
private int selectedIndex;
private int currentSelectedIndex;
private JPanel leftContentPane;
private UIScrollBar scrollBar;
private ActionListener comboBoxActionListener;
public CellQuickEditor() {
initComponents();
createPanelBody();
}
private void initComponents() {
EMPTY_LABEL.setPreferredSize(LABEL_DIMENSION);
topContentContainer = initTopContent();
formatPane = createFormatPane();
centerBodyContainer = createCenterBody();
multipleLabelTip = FRWidgetFactory.createLineWrapLabel(Toolkit.i18nText("Fine-Design_Basic_Cell_Element_Multiple_Tip"));
multipleLabelTip.setEnabled(false);
}
private void createPanelBody() {
double p = TableLayout.PREFERRED;
double f = TableLayout.FILL;
double[] columnSize = {f};
if (isScrollAll()) {
double[] scrollAllRowSize = {p, p, p, p};
prepareScrollBar();
multipleLabelTip.setBorder(BorderFactory.createMatteBorder(5, 10, 0, 0, this.getBackground()));
topContentContainer.setBorder(BorderFactory.createMatteBorder(0, 10, 0, 0, this.getBackground()));
formatPane.setBorder(BorderFactory.createMatteBorder(0, 10, 0, 0, this.getBackground()));
centerBodyContainer.setBorder(BorderFactory.createMatteBorder(0, 10, 0, 0, this.getBackground()));
Component[][] components = new Component[][]{
new Component[]{multipleLabelTip},
new Component[]{topContentContainer},
new Component[]{formatPane},
new Component[]{centerBodyContainer}
};
leftContentPane = TableLayoutHelper.createGapTableLayoutPane(components, scrollAllRowSize, columnSize, HGAP, VGAP);
this.setLayout(new CellElementBarLayout(leftContentPane) {
@Override
public void layoutContainer(Container parent) {
maxHeight = CellElementPropertyPane.getInstance().getHeight() - TITLE_HEIGHT;
int beginY;
if ((MAXVALUE - scrollBar.getVisibleAmount()) == 0) {
beginY = 0;
} else {
int preferredHeight = leftContentPane.getPreferredSize().height;
int value = scrollBar.getValue();
beginY = value * (preferredHeight - maxHeight) / (MAXVALUE - scrollBar.getVisibleAmount());
}
int width = parent.getWidth();
int height = parent.getHeight();
if (leftContentPane.getPreferredSize().height > maxHeight) {
leftContentPane.setBounds(0, -beginY, width - SCROLLBAR_WIDTH - CONTENT_PANE_WIDTH_GAP, height + beginY);
} else {
leftContentPane.setBounds(0, 0, width - SCROLLBAR_WIDTH - CONTENT_PANE_WIDTH_GAP, height);
}
leftContentPane.validate();
}
});
this.add(scrollBar);
this.add(leftContentPane);
} else {
double[] scrollContentRowSize = {p, p, p, f};
multipleLabelTip.setBorder(BorderFactory.createMatteBorder(5, 10, 0, 0, this.getBackground()));
topContentContainer.setBorder(BorderFactory.createMatteBorder(0, 10, 0, 10, this.getBackground()));
formatPane.setBorder(BorderFactory.createMatteBorder(0, 10, 0, 10, this.getBackground()));
centerBodyContainer.setBorder(BorderFactory.createMatteBorder(0, 0, 0, 0, this.getBackground()));
Component[][] components = new Component[][]{
new Component[]{multipleLabelTip},
new Component[]{topContentContainer},
new Component[]{formatPane},
new Component[]{centerBodyContainer}
};
this.setLayout(new BorderLayout());
this.add(TableLayoutHelper.createGapTableLayoutPane(components, scrollContentRowSize, columnSize, HGAP, VGAP), BorderLayout.CENTER);
}
}
/**
* 初始化详细信息面板
*
* @return JComponent 待显示的详细信息面板
*/
public abstract JComponent createCenterBody();
/**
* 是否全局具有滚动条
*
* @return boolean 是否全局具有滚动条
*/
public abstract boolean isScrollAll();
/**
* 初始化下拉框中的类型
*
* @return JComponent 待显示的详细信息面板
*/
public abstract Object getComboBoxSelected();
/**
* 刷新
*/
@Override
protected void refresh() {
refreshPanel();
refreshFormatPanel();
refreshDetails();
refreshMultipleDetails();
}
/**
* 多选时默认隐藏与插入元素类型相关的所有面板
* 若其他面板需要扩展多选时可修改的内容
* 通过重写改方法实现显示/隐藏部分组件
*/
public void refreshMultipleDetails() {
centerBodyContainer.setVisible(tc.isSelectedOneCell());
}
private void refreshFormatPanel() {
// 在populate的时候会多次触发AttributeChangeListener事件,
// 导致不断更新单元格格式,因此populate的时候暂时删除listener
formatPane.removeAttributeChangeListener();
if (cellElement != null) {
formatPane.populateBean(cellElement.getStyle());
} else {
formatPane.populateBean(Style.DEFAULT_STYLE);
}
formatPane.restoreAttributeChangeListener();
}
private void refreshPanel() {
changeVisiableState();
CellSelection cs = (CellSelection) tc.getSelection();
ColumnRow columnRow = ColumnRow.valueOf(cs.getColumn(), cs.getRow());
columnRowTextField.setText(columnRow.toString());
cellElement = tc.getEditingElementCase().getTemplateCellElement(cs.getColumn(), cs.getRow());
JTemplate jTemplate = HistoryTemplateListCache.getInstance().getCurrentEditingTemplate();
if (jTemplate != null) {
comboBox.removeActionListener(comboBoxActionListener);
comboBox.removeAllItems();
String[] items = getDefaultComboBoxItems();
for (String item : items) {
comboBox.addItem(item);
}
Object comboBoxSelected = getComboBoxSelected();
if (comboBoxSelected != null) {
comboBox.setSelectedItem(((ShortCut) comboBoxSelected).getMenuKeySet().getMenuKeySetName());
} else {
comboBox.setSelectedIndex(1);
}
currentSelectedIndex = comboBox.getSelectedIndex();
comboBoxActionListener = new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
cellInsertActions = ActionFactory.createCellInsertAction(ElementCasePane.class, tc);
selectedIndex = comboBox.getSelectedIndex();
comboBox.setPopupVisible(false);
comboBox.repaint();
// comboBox.getSelectedIndex()可能返回-1
if (selectedIndex != -1 && selectedIndex < cellInsertActions.length) {
cellInsertActions[selectedIndex].actionPerformed(e);
}
comboBox.setSelectedIndex(currentSelectedIndex);
}
};
comboBox.addActionListener(comboBoxActionListener);
}
}
/**
* 单选多选时切换部分组件的 隐藏/显示 状态
*/
private void changeVisiableState() {
boolean selectedOneCell = tc.isSelectedOneCell();
comboBox.setEnabled(selectedOneCell);
if (selectedOneCell) {
columnRowTextField.setPreferredSize(null);
cellLabel.setPreferredSize(null);
multipleLabelTip.setPreferredSize(HIDDEN_LABEL_DIMENSION);
} else {
columnRowTextField.setPreferredSize(HIDDEN_LABEL_DIMENSION);
cellLabel.setPreferredSize(HIDDEN_LABEL_DIMENSION);
multipleLabelTip.setPreferredSize(null);
}
}
/**
* 刷新详细信息
*/
protected abstract void refreshDetails();
private JPanel initTopContent() {
double p = TableLayout.PREFERRED;
double f = TableLayout.FILL;
double[] columnSize = {GraphHelper.getWidth(Toolkit.i18nText("Fine-Design_Report_Insert_Cell_Element")), f};
double[] rowSize = {p, p};
cellLabel = FRWidgetFactory.createLineWrapLabel(Toolkit.i18nText("Fine-Design_Basic_Cell"));
UILabel insertContentLabel = FRWidgetFactory.createLineWrapLabel(Toolkit.i18nText("Fine-Design_Report_Insert_Cell_Element"));
initCellElementEditComboBox();
Component[][] components = new Component[][]{
new Component[]{cellLabel, columnRowTextField = initColumnRowTextField()},
new Component[]{insertContentLabel, UIComponentUtils.wrapWithBorderLayoutPane(comboBox)},
};
return TableLayoutHelper.createGapTableLayoutPane(components, rowSize, columnSize, HGAP, VGAP);
}
private void prepareScrollBar() {
scrollBar = new UIScrollBar(UIScrollBar.VERTICAL) {
@Override
public int getVisibleAmount() {
int preferredHeight = leftContentPane.getPreferredSize().height;
int e = MAXVALUE * (maxHeight) / preferredHeight;
setVisibleAmount(e);
return e;
}
@Override
public int getMaximum() {
return MAXVALUE;
}
};
scrollBar.addAdjustmentListener(new AdjustmentListener() {
@Override
public void adjustmentValueChanged(AdjustmentEvent e) {
doLayout();
}
});
this.addMouseWheelListener(new MouseWheelListener() {
@Override
public void mouseWheelMoved(MouseWheelEvent e) {
int value = scrollBar.getValue();
value += MOUSE_WHEEL_SPEED * e.getWheelRotation();
scrollBar.setValue(value);
doLayout();
}
});
scrollBar.setPreferredSize(new Dimension(SCROLLBAR_WIDTH + CONTENT_PANE_WIDTH_GAP, this.getHeight()));
scrollBar.setBlockIncrement(SCROLLBAR_WIDTH + CONTENT_PANE_WIDTH_GAP);
scrollBar.setBorder(BorderFactory.createMatteBorder(0, CONTENT_PANE_WIDTH_GAP, 0, 0, this.getBackground()));
}
/**
* 初始化添加按钮
*/
private void initCellElementEditComboBox() {
JTemplate jTemplate = HistoryTemplateListCache.getInstance().getCurrentEditingTemplate();
if (jTemplate == null) {
comboBox = new UIComboBox();
return;
}
final String[] items = getDefaultComboBoxItems();
comboBox = new UIComboBox(items);
final Object comboBoxSelected = getComboBoxSelected();
if (comboBoxSelected != null) {
comboBox.setSelectedItem(((ShortCut) comboBoxSelected).getMenuKeySet().getMenuKeySetName());
} else {
comboBox.setSelectedIndex(1);
}
currentSelectedIndex = comboBox.getSelectedIndex();
comboBox.addActionListener(comboBoxActionListener);
}
private String[] getDefaultComboBoxItems() {
MenuKeySet[] cellInsertActionNames = ActionFactory.createCellInsertActionName();
ArrayList<String> names = new ArrayList<>();
for (MenuKeySet cellInsertActionName : cellInsertActionNames) {
names.add(cellInsertActionName.getMenuKeySetName());
}
return names.toArray(new String[names.size()]);
}
/**
* 初始化单元格域存储当前选择的单元格例A3,B4等
*
* @return 单元格信息文本域
*/
private UITextField initColumnRowTextField() {
final UITextField columnRowTextField = new UITextField(4);
// barry:输入位置定位单元格
columnRowTextField.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
ColumnRow columnRowEdit = ColumnRow.valueOf(columnRowTextField.getText());
// barry:检查输入是否正确
if (!ColumnRow.validate(columnRowEdit)) {
Object[] options = {Toolkit.i18nText("Fine-Design_Report_OK")};
JOptionPane.showOptionDialog(DesignerContext.getDesignerFrame(), Toolkit.i18nText("Please_Input_Letters+Numbers(A1,AA1,A11....)"), com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Report_Warning"),
JOptionPane.DEFAULT_OPTION, JOptionPane.WARNING_MESSAGE, null, options, options[0]);
// 重新选中当前的selection,把columnRowTextField
tc.setSelection(tc.getSelection());
return;
}
JScrollBar verticalBar = tc.getVerticalScrollBar(), horizontalBar = tc.getHorizontalScrollBar();
int m = columnRowEdit.getColumn(), n = columnRowEdit.getRow();
verticalBar.setMaximum(n);
verticalBar.setValue(n < 21 ? verticalBar.getValue() : n - 20);
horizontalBar.setMaximum(m);
horizontalBar.setValue(m < 13 ? horizontalBar.getValue() : m - 12);
tc.setSelection(new CellSelection(m, n, 1, 1));
tc.requestFocus();
}
});
return columnRowTextField;
}
/**
* 创建格式化面板
*/
private TextFormatPaneContainer createFormatPane() {
TextFormatPaneContainer formatPane = new TextFormatPaneContainer();
AttributeChangeListener attributeChangeListener = new AttributeChangeListener() {
@Override
public void attributeChange() {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
// 耗时任务放线程中,让其他UI组件更新界面,
// 防止多次调用此处每次获取的界面值不同,导致不断更新单元格格式
isEditing = true;
boolean updateStyle = false;
CellSelection cs = (CellSelection) tc.getSelection();
TemplateElementCase editingElementCase = tc.getEditingElementCase();
Set<TemplateCellElement> allCellElements = cs.getAllCellElements(editingElementCase);
Style oldStyle = cellElement == null ? Style.DEFAULT_STYLE : cellElement.getStyle();
Style style = formatPane.update(oldStyle);
for (TemplateCellElement cellElement : allCellElements) {
Style cellElementStyle = cellElement.getStyle();
Format elementFormat = cellElementStyle.getFormat();
Format paneFormat = style.getFormat();
if (!Objects.equals(paneFormat, elementFormat)) {
// 点击单元格,但未设置格式时,不将单元格设置为编辑状态,防止将所选的每个单元格都设置为编辑状态
editingElementCase.addCellElement(cellElement);
Style newStyle = cellElementStyle.deriveFormat(paneFormat);
cellElement.setStyle(newStyle);
updateStyle = true;
}
}
if (updateStyle) {
// 防止频繁触发保存
if (!tc.isSelectedOneCell()) {
record(multipleOperationType.FORMAT);
}
fireTargetModified();
}
isEditing = false;
}
});
}
};
formatPane.addAttributeChangeListener(attributeChangeListener);
return formatPane;
}
/**
* 用于适配记录批量操作的埋点数据
* 真正的埋点提交方案在云端运维插件中
*
* @param type 批量操作修改类型
* @return 批量修改类型名称用于云端运维埋点记录
*/
public static String record(multipleOperationType type) {
return type.getType();
}
/**
* 批量操作的类型
*/
public enum multipleOperationType {
/**
* 批量修改格式
*/
FORMAT("format"),
/**
* 批量修改数据列的数据设置
*/
TYPE_OF_DATA("type-of-data"),
/**
* 批量修改过滤条件
*/
FILTER("filter"),
/**
* 批量修改行高列宽
*/
HEIHT_AND_COLUMN("row-height-and-column-width");
private final String type;
multipleOperationType(String type) {
this.type = type;
}
public String getType() {
return type;
}
}
}