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

577 lines
21 KiB

/*
* Copyright(c) 2001-2010, FineReport Inc, All Rights Reserved.
*/
package com.fr.design.file;
import com.fr.base.FRContext;
import com.fr.design.ExtraDesignClassManager;
import com.fr.design.dialog.FineJOptionPane;
import com.fr.design.fun.TemplateTreeDefineProcessor;
import com.fr.design.gui.itree.filetree.TemplateFileTree;
import com.fr.design.gui.itree.refreshabletree.ExpandMutableTreeNode;
import com.fr.design.i18n.Toolkit;
import com.fr.design.layout.FRGUIPaneFactory;
import com.fr.design.lock.LockInfoUtils;
import com.fr.design.mainframe.DesignerContext;
import com.fr.design.mainframe.DesignerFrameFileDealerPane;
import com.fr.design.lock.LockInfoDialog;
import com.fr.design.mainframe.JTemplate;
import com.fr.design.mainframe.manager.search.TemplateTreeSearchManager;
import com.fr.design.mainframe.manager.search.searcher.control.pane.TemplateSearchRemindPane;
import com.fr.design.mainframe.vcs.common.VcsHelper;
import com.fr.file.FILE;
import com.fr.file.FileNodeFILE;
import com.fr.file.filetree.FileNode;
import com.fr.file.filetree.IOFileNodeFilter;
import com.fr.form.fit.web.editpreview.FileLockStateObservable;
import com.fr.general.ComparatorUtils;
import com.fr.general.IOUtils;
import com.fr.log.FineLoggerFactory;
import com.fr.workspace.base.UserInfo;
import com.fr.stable.ArrayUtils;
import com.fr.stable.CoreConstants;
import com.fr.stable.StableUtils;
import com.fr.stable.project.ProjectConstants;
import com.fr.workspace.WorkContext;
import com.fr.report.lock.LockInfoOperator;
import java.util.UUID;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.SwingWorker;
import javax.swing.ToolTipManager;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.TreeNode;
import javax.swing.tree.TreePath;
import java.awt.BorderLayout;
import java.awt.Desktop;
import java.awt.Dimension;
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.File;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Enumeration;
import java.util.Objects;
import java.util.Observable;
import java.util.Observer;
import java.util.Set;
import java.util.concurrent.ExecutionException;
import java.util.stream.Collectors;
import com.fr.workspace.server.vcs.VcsOperator;
import org.jetbrains.annotations.Nullable;
import static javax.swing.JOptionPane.WARNING_MESSAGE;
import static javax.swing.JOptionPane.YES_NO_OPTION;
public class TemplateTreePane extends JPanel implements FileOperations {
public static TemplateTreePane getInstance() {
return HOLDER.singleton;
}
private static class HOLDER {
private static TemplateTreePane singleton = new TemplateTreePane();
}
private TemplateFileTree reportletsTree;
private TemplateSearchRemindPane remindPane;
private FileToolbarStateChangeListener toolBarStateChangeListener;
private TemplateTreePane() {
this.setLayout(FRGUIPaneFactory.createBorderLayout());
this.setPreferredSize(new Dimension(250, super.getPreferredSize().height));
reportletsTree = new TemplateFileTree();
remindPane = new TemplateSearchRemindPane(getTemplateFileTree());
this.add(remindPane, BorderLayout.CENTER);
ToolTipManager.sharedInstance().registerComponent(reportletsTree);
FileLockStateObservable.getInstance().addObserver(new Observer() {
@Override
public void update(Observable o, Object arg) {
TemplateTreePane.this.refresh();
}
});
/*
* Tree.MouseAdapter
*/
MouseListener mouseFileTreeListener = new MouseAdapter() {
@Override
public void mousePressed(MouseEvent evt) {
if (reportletsTree.getPathForLocation(evt.getX(), evt.getY()) != null && evt.getClickCount() == 2) {
openFile();
}
}
@Override
public void mouseClicked(MouseEvent e) {
if (SwingUtilities.isRightMouseButton(e)) {
TemplateTreeDefineProcessor processor = ExtraDesignClassManager.getInstance().getSingle(TemplateTreeDefineProcessor.XML_TAG);
if (processor != null) {
processor.rightClickAction(e);
} else {
DefaultTemplateTreeDefineProcessor.getInstance().rightClickAction(e);
}
}
}
@Override
public void mouseReleased(MouseEvent e) {
if (toolBarStateChangeListener != null) {
toolBarStateChangeListener.stateChange();
}
DesignerFrameFileDealerPane.getInstance().refreshRightToolBarBy(reportletsTree.getSelectedFileNode());
}
};
// lx: add mouse listener
this.reportletsTree.addMouseListener(mouseFileTreeListener);
this.reportletsTree.addKeyListener(new KeyListener() {
@Override
public void keyPressed(KeyEvent e) {
if (e.getKeyCode() == KeyEvent.VK_ENTER) {
openFile();
}
}
@Override
public void keyReleased(KeyEvent e) {
if (e.getKeyCode() == KeyEvent.VK_ENTER) {
openFile();
}
if (toolBarStateChangeListener != null) {
toolBarStateChangeListener.stateChange();
}
}
@Override
public void keyTyped(KeyEvent e) {
if (e.getKeyCode() == KeyEvent.VK_ENTER) {
openFile();
}
}
});
}
public TemplateFileTree getTemplateFileTree() {
return this.reportletsTree;
}
/**
* 选中的路径数
*
* @return 选中的路径数
*/
public int countSelectedPath() {
TreePath[] treePaths = reportletsTree.getSelectionPaths();
if (treePaths == null) {
return 0;
}
return treePaths.length;
}
/**
* 选中的文件夹个数
*
* @return 选中的文件夹个数
*/
public int countSelectedFolder() {
if (reportletsTree.getSelectedFolderPaths() == null) {
return 0;
}
return reportletsTree.getSelectedFolderPaths().length;
}
/**
* 选中的文件个数
*
* @return 选中的文件
*/
public int countSelectedFile() {
if (reportletsTree.getSelectionPaths() == null) {
return 0;
}
return reportletsTree.getSelectedTemplatePaths().length;
}
/**
* 刷新
*/
public void refreshDockingView() {
reportletsTree.setFileNodeFilter(new IOFileNodeFilter(FRContext.getFileNodes().getSupportedTypes()));
reportletsTree.refreshEnv();
}
@Override
public boolean mkdir(String path) {
return TemplateResourceManager.getResource().mkdir(path);
}
/**
* 打开选中的报表文件
*/
@Override
public void openFile() {
// 判断是否是远程设计的锁定文件
FileNode node = reportletsTree.getSelectedFileNode();
if (node == null) {
return;
}
String reportPath = reportletsTree.getSelectedTemplatePath();
final String selectedFilePath = StableUtils.pathJoin(ProjectConstants.REPORTLETS_NAME, reportPath);
String lock = node.getLock();
boolean showLockInfo = needShowLockInfo(lock, selectedFilePath, node);
if (showLockInfo) {
UserInfo userInfo = WorkContext.getCurrent().get(LockInfoOperator.class).getUserInfo(selectedFilePath);
node.setLock(UUID.randomUUID().toString());
// 对于开发者预览占位锁定 定位到tab中
checkDevelopForBiddenTemplate(selectedFilePath);
LockInfoDialog.show(userInfo);
return;
} else {
node.setLock(null);
}
DesignerContext.getDesignerFrame().openTemplate(new FileNodeFILE(new FileNode(selectedFilePath, false)));
}
/**
* 是否需要展示锁定信息
*/
public static boolean needShowLockInfo(String lock, String selectedFilePath, FileNode node) {
return LockInfoUtils.isCompatibleOperator()
|| LockInfoUtils.unableGetLockInfo()
|| WorkContext.getCurrent().get(LockInfoOperator.class).isTplUnLocked(selectedFilePath)
? (lock != null && !lock.equals(node.getUserID()))
: WorkContext.getCurrent().get(LockInfoOperator.class).isTplLocked(selectedFilePath);
}
private void checkDevelopForBiddenTemplate(String selectedFilePath) {
JTemplate<?, ?> template = getOpenedTemplate(selectedFilePath);
if (template != null && template.isForbidden()) {
DesignerContext.getDesignerFrame().openTemplate(new FileNodeFILE(new FileNode(selectedFilePath, false)));
}
}
@Nullable
private JTemplate<?, ?> getOpenedTemplate(String path) {
for (JTemplate<?, ?> template : HistoryTemplateListCache.getInstance().getHistoryList()) {
if (ComparatorUtils.equals(template.getEditingFILE().getPath(), path)) {
return template;
}
}
return null;
}
/**
* 打开文件夹
*/
@Override
public void showInExplorer() {
FileNode fn = TemplateTreePane.this.reportletsTree.getSelectedFileNode();
String filePath = StableUtils.pathJoin(WorkContext.getCurrent().getPath(), fn.getEnvPath());
filePath = filePath.substring(0, filePath.lastIndexOf(CoreConstants.SEPARATOR));
try {
Desktop.getDesktop().open(new File(filePath));
} catch (Exception e) {
IOUtils.openWindowsFolder(StableUtils.pathJoin(WorkContext.getCurrent().getPath(), fn.getEnvPath()));
}
}
/**
* 刷新
*/
@Override
public void refresh() {
// 刷新远程文件夹权限
NodeAuthProcessor.getInstance().refresh();
reportletsTree.refresh();
DesignerFrameFileDealerPane.getInstance().refreshRightToolBarBy(null);
FineLoggerFactory.getLogger().info(Toolkit.i18nText("Fine-Design_Basic_Template_File_Tree_Refresh_Successfully") + "!");
}
/**
* 刷新父目录
*/
@Override
public void refreshParent() {
// 刷新远程文件夹权限
NodeAuthProcessor.getInstance().refresh();
if (reportletsTree.getSelectionCount() == 0) {
//没选中文件刷新根目录
reportletsTree.refresh();
return;
}
reportletsTree.refreshParent(Objects.requireNonNull(reportletsTree.getSelectionPath()));
DesignerFrameFileDealerPane.getInstance().refreshRightToolBarBy(null);
FineLoggerFactory.getLogger().info(Toolkit.i18nText("Fine-Design_Basic_Template_File_Tree_Refresh_Successfully"));
}
/**
* 删除文件
* 文件夹和文件均可删除
* <p>
* 当文件被锁时不能删除
* 当文件夹中包含被锁文件时不能删除
*/
@Override
public void deleteFile() {
ExpandMutableTreeNode[] treeNodes = reportletsTree.getSelectedTreeNodes();
// 筛选可以删除的文件
ArrayList<ExpandMutableTreeNode> deletableNodes = new ArrayList<>();
ArrayList<ExpandMutableTreeNode> lockedNodes = new ArrayList<>();
for (ExpandMutableTreeNode treeNode : treeNodes) {
FileOperationHelper.getInstance().checkFreeOrLock(treeNode, deletableNodes, lockedNodes);
}
if (lockedNodes.isEmpty()) {
String tipContent =
countSelectedFolder() > 0
? Toolkit.i18nText("Fine-Design_Basic_Confirm_Delete_Folder")
: Toolkit.i18nText("Fine-Design_Basic_Confirm_Delete_File");
if (FineJOptionPane.showConfirmDialog(DesignerContext.getDesignerFrame(),
tipContent,
Toolkit.i18nText("Fine-Design_Basic_Dialog_Prompt"),
YES_NO_OPTION)
== JOptionPane.YES_OPTION) {
// 删除所有选中的即可
deleteNodes(Arrays.asList(treeNodes));
}
} else {
String tipContent =
countSelectedFolder() > 0
? Toolkit.i18nText("Fine-Design_Basic_Confirm_Delete_Unlock_File_And_Folder")
: Toolkit.i18nText("Fine-Design_Basic_Confirm_Delete_Unlock_File");
if (deletableNodes.isEmpty()) {
// 提醒被锁定模板无法删除
FineJOptionPane.showMessageDialog(DesignerContext.getDesignerFrame(),
Toolkit.i18nText("Fine-Design_Basic_Unable_Delete_Locked_File"),
Toolkit.i18nText("Fine-Design_Basic_Alert"),
WARNING_MESSAGE);
return;
}
if (FineJOptionPane.showConfirmDialog(DesignerContext.getDesignerFrame(),
tipContent,
Toolkit.i18nText("Fine-Design_Basic_Dialog_Prompt"),
YES_NO_OPTION)
== JOptionPane.YES_OPTION) {
// 删除其他
deleteNodes(deletableNodes);
}
}
}
private void refreshAfterDelete(Set<FileNode> deletedPaths) {
if (TemplateTreeSearchManager.getInstance().isInSearchMode()) {
TemplateTreeSearchManager.getInstance().deleteMatchedNode(deletedPaths);
TemplateTreeSearchManager.getInstance().updateTemplateTree();
TemplateTreeSearchManager.getInstance().setRefreshing(false);
} else {
TreePath[] paths = reportletsTree.getSelectionPaths();
if (paths == null) {
reportletsTree.refresh();
} else {
// 删除一个节点的情况下,默认选中为该节点的上一级节点
if (paths.length == 1 && paths[0].getParentPath() != null) {
reportletsTree.setSelectionPath(paths[0].getParentPath());
}
for (TreePath path : Objects.requireNonNull(paths)) {
reportletsTree.refreshParent(path);
}
}
}
}
private void deleteNodes(Collection<ExpandMutableTreeNode> nodes) {
new SwingWorker<Boolean,Void>(){
@Override
protected Boolean doInBackground() throws Exception {
boolean success = true;
for (ExpandMutableTreeNode treeNode : nodes) {
Object node = treeNode.getUserObject();
if (node instanceof FileNode) {
FileNodeFILE nodeFILE = new FileNodeFILE((FileNode) node);
if (nodeFILE.exists()) {
if (!VcsHelper.getInstance().isLegacyMode()) {
try {
WorkContext.getCurrent().get(VcsOperator.class).recycleVersion(VcsHelper.getInstance().getCurrentUsername(), nodeFILE.getPath());
} catch (Exception e) {
FineLoggerFactory.getLogger().error("[VcsV2] recycle {} failed", nodeFILE.getName());
}
}
if (TemplateResourceManager.getResource().delete(nodeFILE)) {
HistoryTemplateListCache.getInstance().deleteFile(nodeFILE);
} else {
success = false;
}
}
}
}
return success;
}
@Override
protected void done() {
try {
if (!get()) {
showErrorDialog();
}
Set<FileNode> deletedFileNode = nodes.stream().map(treeNode -> (FileNode) treeNode.getUserObject()).collect(Collectors.toSet());
refreshAfterDelete(deletedFileNode);
} catch (InterruptedException | ExecutionException e) {
throw new RuntimeException(e);
}
}
}.execute();
}
private void showErrorDialog() {
FineJOptionPane.showConfirmDialog(DesignerContext.getDesignerFrame(),
Toolkit.i18nText("Fine-Design_Basic_Delete_Failure"),
Toolkit.i18nText("Fine-Design_Basic_Error"),
JOptionPane.DEFAULT_OPTION,
JOptionPane.ERROR_MESSAGE);
}
@Override
public void lockFile() {
throw new UnsupportedOperationException("unsupport now");
}
@Override
public void unlockFile() {
throw new UnsupportedOperationException("unsupport now");
}
@Override
public String getFilePath() {
return reportletsTree.getSelectedTemplatePath();
}
@Override
public boolean access() {
TreePath[] selectedTreePaths = reportletsTree.getSelectionPaths();
if (ArrayUtils.isEmpty(selectedTreePaths)) {
return false;
}
// 选中的是文件夹
TreePath treePath = selectedTreePaths[0];
ExpandMutableTreeNode currentTreeNode = (ExpandMutableTreeNode) treePath.getLastPathComponent();
ExpandMutableTreeNode parentTreeNode = (ExpandMutableTreeNode) currentTreeNode.getParent();
return parentTreeNode != null && parentTreeNode.hasFullAuthority();
}
@Override
public boolean rename(FILE tplFile, String from, String to) {
// 多人协作时判断是否有锁定的文件
ExpandMutableTreeNode[] treeNodes = reportletsTree.getSelectedTreeNodes();
// 筛选可以重命名的文件
ArrayList<ExpandMutableTreeNode> unlockedNodes = new ArrayList<>();
ArrayList<ExpandMutableTreeNode> lockedNodes = new ArrayList<>();
for (ExpandMutableTreeNode treeNode : treeNodes) {
FileOperationHelper.getInstance().checkFreeOrLock(treeNode, unlockedNodes, lockedNodes);
}
if (!lockedNodes.isEmpty()) {
FineJOptionPane.showConfirmDialog(DesignerContext.getDesignerFrame(),
Toolkit.i18nText("Fine-Design_Basic_Warn_Rename_Lock_File"),
Toolkit.i18nText("Fine-Design_Basic_Alert"),
JOptionPane.DEFAULT_OPTION, JOptionPane.WARNING_MESSAGE);
return true;
}
try {
// 接收的是WEB-INF下的路径
return TemplateResourceManager.getResource().rename(from, to);
} catch (Exception e) {
FineLoggerFactory.getLogger().error(e.getMessage(), e);
return false;
}
}
@Override
public FileNode getFileNode() {
return reportletsTree.getSelectedFileNode();
}
public void setToolbarStateChangeListener(FileToolbarStateChangeListener listener) {
this.toolBarStateChangeListener = listener;
}
/**
* 仅支持在拥有完整权限的文件夹下进行新建和重命名操作,那么是可以看到改文件夹下所有文件的。
*
* @param newName 原名
* @param suffix 后缀名
* @param baseOnSelf 检验目录时,是基于自身目录的子节点还是基于父目录的子节点进行校验
* @return 是否有重名的
*/
@Override
public boolean duplicated(String newName, String suffix, boolean baseOnSelf) {
// 选中的节点
TreePath treePath = reportletsTree.getSelectionPath();
if (treePath == null) {
return false;
}
boolean result = false;
Enumeration children = getChild(treePath, baseOnSelf);
while (children.hasMoreElements()) {
DefaultMutableTreeNode childNode = (DefaultMutableTreeNode) children.nextElement();
Object object = childNode.getUserObject();
if (object instanceof FileNode) {
if (ComparatorUtils.equals(((FileNode) object).getName(), newName + suffix)) {
result = true;
}
}
}
return result;
}
private Enumeration getChild(TreePath treePath, boolean baseOnSelf) {
DefaultMutableTreeNode currentTreeNode = (DefaultMutableTreeNode) treePath.getLastPathComponent();
Enumeration children;
//现在可以在目录下创建目录,重命名时是基于所选节点的父目录的子目录进行校验,目录下新建子目录时,基于所选目录的子目录进行校验
if (reportletsTree.getSelectedFileNode().isDirectory() && baseOnSelf) {
children = currentTreeNode.children();
} else {
TreeNode parentTreeNode = currentTreeNode.getParent();
children = parentTreeNode.children();
}
return children;
}
}