帆软报表设计器源代码。
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.
 
 
 
 

587 lines
19 KiB

package com.fr.design.mainframe;
import com.fr.design.constants.UIConstants;
import com.fr.design.designer.creator.XCreator;
import com.fr.design.designer.creator.XCreatorUtils;
import com.fr.design.designer.creator.XLayoutContainer;
import com.fr.design.designer.creator.XWAbsoluteLayout;
import com.fr.design.designer.creator.XWTitleLayout;
import com.fr.design.designer.creator.cardlayout.XWCardMainBorderLayout;
import com.fr.design.designer.treeview.ComponentTreeCellRenderer;
import com.fr.design.designer.treeview.ComponentTreeModel;
import com.fr.design.file.HistoryTemplateListCache;
import com.fr.design.gui.itree.UITreeUI;
import com.fr.design.utils.ComponentUtils;
import com.fr.design.utils.gui.GUICoreUtils;
import com.fr.stable.StringUtils;
import java.awt.Rectangle;
import java.util.Stack;
import java.util.function.Consumer;
import javax.swing.BorderFactory;
import javax.swing.DropMode;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.JTree;
import javax.swing.SwingUtilities;
import javax.swing.tree.TreeCellRenderer;
import javax.swing.tree.TreePath;
import javax.swing.tree.TreeSelectionModel;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Point;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class ComponentTree extends JTree {
private FormDesigner designer;
private ComponentTreeModel model;
private UITreeUI uiTreeUI = new UITreeUI();
private PopupPreviewPane previewPane;
private static final Map<String, List<TreePath>> treePathCache = new HashMap<>();
private static final int PADDING_LEFT = 5;
private static final int PADDING_TOP = 5;
public ComponentTree(FormDesigner designer) {
this.designer = designer;
this.setBackground(UIConstants.TREE_BACKGROUND);
setRootVisible(true);
setCellRenderer(new ComponentTreeCellRenderer());
getSelectionModel().setSelectionMode(TreeSelectionModel.DISCONTIGUOUS_TREE_SELECTION);
this.setDragEnabled(false);
this.setDropMode(DropMode.ON_OR_INSERT);
this.setTransferHandler(new TreeTransferHandler());
this.refreshTreeRoot();
initListeners();
setEditable(true);
setUI(uiTreeUI);
setBorder(BorderFactory.createEmptyBorder(PADDING_TOP, PADDING_LEFT, 0, 0));
}
private void initListeners() {
this.addTreeSelectionListener(designer);
ComponetTreeMouseListener componetTreeMouseListener = new ComponetTreeMouseListener(this);
this.addMouseMotionListener(componetTreeMouseListener);
this.addMouseListener(componetTreeMouseListener);
}
public FormDesigner getDesigner() {
return designer;
}
/**
* 构造函数
*
* @param designer 设计界面组件
* @param model 构造JTree的model
*/
public ComponentTree(FormDesigner designer, ComponentTreeModel model) {
this(designer);
this.setModel(model);
expandTree();
}
public void setSelectionPath(TreePath path) {
// 不管点击哪一项,都要先退出编辑状态(图表、报表块、绝对布局、tab块)
Object widget = path.getLastPathComponent();
if(widget == null ||!((XCreator)widget).getXCreatorBaseOperate().supportSelected()){
return;
}
designer.stopEditing(path);
super.setSelectionPath(path);
}
@Override
public void removeAll() {
if (previewPane != null) {
previewPane.setVisible(false);
previewPane.removeAll();
}
super.removeAll();
}
/**
* 是否可编辑
*
* @param path 树路径
* @return 是则返回true
*/
@Override
public boolean isPathEditable(TreePath path) {
return false;
}
/**
* 将值转换为文本
*
* @param value 值
* @param selected 是否选中
* @param expanded 扩展
* @param leaf 是否叶子
* @param row 行
* @param hasFocus 是否焦点
* @return 返回文本
*/
@Override
public String convertValueToText(Object value, boolean selected, boolean expanded, boolean leaf, int row, boolean hasFocus) {
if (value != null && value instanceof XCreator) {
return ((XCreator) value).toData().getWidgetName();
} else {
return super.convertValueToText(value, selected, expanded, leaf, row, hasFocus);
}
}
public void setAndScrollSelectionPath(TreePath[] treepath) {
setSelectionPaths(treepath);
if (treepath.length > 0) {
scrollPathToVisible(treepath[0]);
}
}
@Override
public void paint(Graphics g) {
super.paint(g);
designer.getSelectionModel(); // 否则参数一个一个加会导致参数面板和body结构出问题
}
/**
* 刷新
*/
public void refreshUI() {
updateUI();
setUI(uiTreeUI);
}
public TreePath[] getSelectedTreePath() {
XCreator[] creators = designer.getSelectionModel().getSelection().getSelectedCreators();
TreePath[] paths = new TreePath[creators.length];
for (int i = 0; i < paths.length; i++) {
paths[i] = buildTreePath(creators[i]);
}
return paths;
}
/**
* 搜索指定名称的路径
*
* @param text 名称
* @return 树路径
*/
public TreePath[] search(String text) {
if (StringUtils.isNotEmpty(text)) {
text = text.toLowerCase();
}
ArrayList<XCreator> searchList = new ArrayList<XCreator>();
XLayoutContainer root = designer.getRootComponent();
searchFormRoot(root, searchList, text);
TreePath[] paths = new TreePath[searchList.size()];
for (int i = 0; i < paths.length; i++) {
paths[i] = buildTreePath(searchList.get(i));
}
if (paths.length > 0) {
setAndScrollSelectionPath(paths);
} else {
setSelectionPath();
}
return paths;
}
@Override
protected void setExpandedState(TreePath path, boolean state) {
super.setExpandedState(path, state);
saveTreePath();
}
/**
* 删除key对应的缓存展开路径
*/
public void removeTreePath(String key) {
if (StringUtils.isNotEmpty(key)) {
treePathCache.remove(key);
}
}
private void setSelectionPath() {
/**
* 不让传null参数,所以只有自己定义
*/
super.setSelectionPath(null);
clearSelection();
}
private void searchFormRoot(XLayoutContainer container, ArrayList<XCreator> searchList, String text) {
if (StringUtils.isEmpty(text)) {
return;
}
for (int i = 0, size = container.getXCreatorCount(); i < size; i++) {
XCreator creator = container.getXCreator(i);
String xName = creator.toData().getWidgetName();
if (xName.toLowerCase().contains(text)) {
searchList.add(creator);
}
if (creator instanceof XLayoutContainer) {
searchFormRoot((XLayoutContainer) creator, searchList, text);
}
}
}
/**
* 触发
*/
public void fireTreeChanged() {
designer.refreshDesignerUI();
}
/**
* 刷新
*/
public void refreshTreeRoot() {
model = new ComponentTreeModel(designer, designer.getTopContainer());
setModel(model);
expandTree();
setDragEnabled(false);
setDropMode(DropMode.ON_OR_INSERT);
setTransferHandler(new TreeTransferHandler());
repaint();
}
/**
* 从缓存中获取展开路径并进行展开
*/
public void expandTree() {
expandTree(loadTreePath());
}
/**
* 按照传入参数展开组件树
*/
private void expandTree(List<TreePath> list) {
if (list == null) {
return;
}
for (TreePath treePath : list) {
expandPath(treePath);
}
}
/**
* 获得树的展开路径
*/
private List<TreePath> getExpandTreePaths() {
List<TreePath> result = new ArrayList<>();
TreePath rootTreePath = buildTreePath(designer.getTopContainer());
Enumeration<TreePath> enumeration = getExpandedDescendants(rootTreePath);
if (enumeration != null) {
while (enumeration.hasMoreElements()) {
result.add(enumeration.nextElement());
}
}
return result;
}
private void saveTreePath() {
JTemplate<?, ?> jt = HistoryTemplateListCache.getInstance().getCurrentEditingTemplate();
if (jt instanceof JForm && jt.getTarget() == designer.getTarget()) {
String key = jt.getEditingFILE().getPath() + "-" + designer.getTarget().getTemplateID();
treePathCache.put(key, getExpandTreePaths());
}
}
private List<TreePath> loadTreePath() {
List<TreePath> result = null;
JTemplate<?, ?> jt = HistoryTemplateListCache.getInstance().getCurrentEditingTemplate();
if (jt instanceof JForm) {
String key = jt.getEditingFILE().getPath() + "-" + designer.getTarget().getTemplateID();
result = treePathCache.get(key);
}
return result == null ? new ArrayList<>() : result;
}
private TreePath buildTreePath(Component comp) {
ArrayList<Component> path = new ArrayList<Component>();
Component parent = comp;
while (parent != null) {
XCreator creator = (XCreator) parent;
path.add(0, parent);
if (creator != comp) {
creator.notShowInComponentTree(path);
}
parent = creator.getParentShow();
}
Object[] components = path.toArray();
return new TreePath(components);
}
private void popupPreviewPane(int popupPosYOnScreen, XCreator comp) {
if (previewPane == null) {
previewPane = new PopupPreviewPane();
}
if (previewPane.isVisible() && previewPane.getLocationOnScreen().y != popupPosYOnScreen) {
previewPane.setVisible(false);
}
if (!previewPane.isVisible() && comp.getWidth() != 0 && comp.getHeight() != 0) {
previewPane.setComp(comp);
int popupPosY = popupPosYOnScreen - FormHierarchyTreePane.getInstance().getLocationOnScreen().y;
GUICoreUtils.showPopupMenu(previewPane, FormHierarchyTreePane.getInstance(), -previewPane.getPreferredSize().width, popupPosY);
}
}
private void hidePreviewPane() {
if (previewPane != null && previewPane.isVisible()) {
previewPane.setVisible(false);
}
}
private final class ComponetTreeMouseListener extends MouseAdapter {
private final JTree tree;
private XCreator selectedCreator;
private ComponetTreeMouseListener(JTree tree) {
this.tree = tree;
}
@Override
public void mouseMoved(MouseEvent e) {
Point p = e.getPoint();
int selRow = tree.getRowForLocation(p.x, p.y);
TreeCellRenderer r = tree.getCellRenderer();
if (selRow != -1 && r != null) {
TreePath path = tree.getPathForRow(selRow);
Point point = tree.getPathBounds(path).getLocation();
SwingUtilities.convertPointToScreen(point, tree);
XCreator comp = (XCreator) path.getLastPathComponent();
if (comp.getXCreatorBaseOperate().supportSelected()) {
popupPreviewPane(point.y, comp);
return;
}
}
hidePreviewPane();
}
@Override
public void mouseExited(MouseEvent e) {
hidePreviewPane();
}
@Override
public void mouseClicked(MouseEvent e) {
onMouseEvent(e, new Consumer<XCreator>() {
@Override
public void accept(XCreator creator) {
if (e.getClickCount() == 2 && e.getButton() == MouseEvent.BUTTON1 && !designer.isFormParaDesigner()
&&creator.getXCreatorBaseOperate().supportSelected()) {
startEditing(creator);
}
}
});
}
@Override
public void mousePressed(MouseEvent e) {
onMouseEvent(e, new Consumer<XCreator>() {
@Override
public void accept(XCreator creator) {
if (e.getButton() == MouseEvent.BUTTON1 && creator.getXCreatorBaseOperate().supportSelected()) {
selectedCreator = creator;
}
}
});
}
@Override
public void mouseReleased(MouseEvent e) {
if (e.isControlDown() || e.isShiftDown()) {
return;
}
XCreator currentCreator = designer.getSelectionModel().getSelection().getSelectedCreator();
// 以当前选中的为准
if (currentCreator != selectedCreator) {
selectedCreator = currentCreator;
}
if (e.getButton() == MouseEvent.BUTTON1 && selectedCreator != null) {
showSelectedPopup(selectedCreator);
}
}
private void showSelectedPopup(XCreator comp) {
Rectangle rectangle = getRelativeBounds(comp);
comp.showSelectedPopup(designer, rectangle, comp.acceptType(XWTitleLayout.class, XWCardMainBorderLayout.class, XWAbsoluteLayout.class));
comp.setSelected(true);
designer.repaint();
}
/**
* 响应鼠标事件并解析选中的组件
*
* @param e
* @param consumer
*/
private void onMouseEvent(final MouseEvent e, Consumer<XCreator> consumer) {
if (e.isControlDown() || e.isShiftDown()) {
return;
}
Point p = e.getPoint();
// 解析组件树路径 获取选中的组件
int selRow = tree.getRowForLocation(p.x, p.y);
TreePath path = tree.getPathForRow(selRow);
Rectangle bounds = tree.getPathBounds(path);
if (bounds != null) {
Point point = bounds.getLocation();
SwingUtilities.convertPointToScreen(point, tree);
XCreator comp = (XCreator) path.getLastPathComponent();
consumer.accept(comp);
}
}
/**
* 组件进入编辑状态
*
* @param comp
*/
private void startEditing(XCreator comp) {
designer.getSelectionModel().selectACreator(comp);
XCreator creator = comp.getEditingChildCreator();
responseEditing(creator);
// 放到事件尾部执行
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
//处理下tab块的选中
if (comp.acceptType(XWCardMainBorderLayout.class)) {
XCreator xCreator = designer.getSelectionModel().getSelection().getSelectedCreator();
if (xCreator != null) {
showSelectedPopup(xCreator);
}
}
}
});
}
/**
* 自父容器到子组件 每一层的都响应click事件
*
*
* @param creator
*/
private void responseEditing(XCreator creator) {
Stack<XCreator> stack = new Stack<>();
stack.push(creator);
while (creator.getParent() instanceof XCreator) {
creator = (XCreator) creator.getParent();
stack.push(creator);
}
while (!stack.isEmpty()) {
stack.pop().startEditing();
}
}
/**
* 获取组件范围坐标
*
* @param creator
* @return
*/
private Rectangle getRelativeBounds(XCreator creator) {
Rectangle bounds = creator.getBounds();
XLayoutContainer parent = XCreatorUtils.getParentXLayoutContainer(creator);
if (parent == null) {
return bounds;
}
Rectangle rec = ComponentUtils.getRelativeBounds(parent);
bounds.x += rec.x;
bounds.y += rec.y;
return bounds;
}
}
private class PopupPreviewPane extends JPopupMenu {
private Container contentPane;
private BufferedImage compImage;
private static final int MAX_WIDTH = 360;
private static final int MAX_HEIGHT = 280;
PopupPreviewPane() {
contentPane = new JPanel();
contentPane.setBackground(Color.white);
this.setLayout(new BorderLayout());
this.add(contentPane, BorderLayout.CENTER);
this.setOpaque(false);
setPreferredSize(new Dimension(MAX_WIDTH, MAX_HEIGHT));
setBorder(BorderFactory.createLineBorder(UIConstants.LINE_COLOR));
}
public void setComp(XCreator comp) {
try {
this.compImage = componentToImage(comp);
this.updateSize();
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public void paint(Graphics g) {
super.paint(g);
if (compImage != null) {
g.drawImage(compImage, 0, 0, getWidth(), getHeight(), null);
}
}
@Override
public void setVisible(boolean visible) {
super.setVisible(visible);
}
public void menuSelectionChanged(boolean isIncluded) {
}
public Container getContentPane() {
return contentPane;
}
private BufferedImage componentToImage(Component comp) throws IOException {
BufferedImage im = new BufferedImage(comp.getWidth(), comp.getHeight(), BufferedImage.TYPE_INT_ARGB);
comp.paint(im.getGraphics());
return im;
}
// 根据控件内容,更新弹出框大小
private void updateSize() {
int width = compImage.getWidth();
int height = compImage.getHeight();
double aspectRatio = (double) width / height;
if (width > MAX_WIDTH) {
width = MAX_WIDTH;
height = (int) (width / aspectRatio);
}
if (height > MAX_HEIGHT) {
height = MAX_HEIGHT;
width = (int) (height * aspectRatio);
}
this.setPreferredSize(new Dimension(width, height));
}
}
}