From a4a20d1440ade9f17e33496d87fd69dd2d3356db Mon Sep 17 00:00:00 2001 From: roger Date: Sun, 14 Aug 2022 20:22:26 +0800 Subject: [PATCH] =?UTF-8?q?REPORT-75090=20feat:=E8=AE=BE=E8=AE=A1=E5=99=A8?= =?UTF-8?q?=E6=A8=A1=E6=9D=BF=E6=96=87=E4=BB=B6=E7=AE=A1=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../fr/design/actions/file/DelFileAction.java | 39 ++ .../fr/design/actions/file/LocateAction.java | 136 +++++ .../fr/design/actions/file/RenameAction.java | 323 +++++++++++ .../search/TableDataTreeSearchManager.java | 10 +- .../common/TableDataSearchCallBack.java | 6 +- .../control/common/TableDataSearchResult.java | 2 +- .../control/common/TableDataSearchTask.java | 6 +- .../pre/TableDataPreSearchCallBack.java | 2 +- .../control/pre/TableDataPreSearchResult.java | 2 +- .../control/pre/TableDataPreSearchTask.java | 8 +- .../pane/TableDataSearchRemindPane.java | 6 +- .../search/pane/TreeSearchToolbarPane.java | 7 +- .../searcher/TableDataTreeSearcher.java | 2 + .../DefaultTemplateTreeDefineProcessor.java | 545 ++++++++++++++++++ .../com/fr/design/file/FileOperations.java | 5 + .../fr/design/file/MutilTempalteTabPane.java | 163 ++++++ .../fr/design/file/TemplateDirTreePane.java | 79 +++ .../com/fr/design/file/TemplateTreePane.java | 45 +- .../gui/itree/filetree/EnvFileTree.java | 73 +-- .../gui/itree/filetree/TemplateDirTree.java | 54 ++ .../gui/itree/filetree/TemplateFileTree.java | 113 +++- .../ExpandMutableTreeNode.java | 8 + .../refreshabletree/RefreshableJTree.java | 5 + .../DesignerFrameFileDealerPane.java | 373 +++--------- .../manager/clip/TemplateTreeClipboard.java | 63 ++ .../search/TemplateDirTreeSearchManager.java | 190 ++++++ .../search/TemplateTreeSearchManager.java | 222 +++++++ .../searcher/TemplateDirTreeSearcher.java | 164 ++++++ .../search/searcher/TemplateTreeSearcher.java | 163 ++++++ .../common/TemplateDirSearchCallBack.java | 49 ++ .../common/TemplateSearchCallBack.java | 59 ++ .../control/common/TemplateSearchResult.java | 127 ++++ .../control/common/TemplateSearchTask.java | 75 +++ .../pane/TemplateDirSearchRemindPane.java | 211 +++++++ .../pane/TemplateDirTreeSearchPane.java | 149 +++++ .../pane/TemplateSearchRemindPane.java | 212 +++++++ .../pane/TemplateTreeSearchToolbarPane.java | 209 +++++++ .../control/pre/TemplateDirPreSearchTask.java | 32 + .../pre/TemplatePreSearchCallBack.java | 26 + .../control/pre/TemplatePreSearchTask.java | 58 ++ .../fr/design/search/TreeSearchManager.java | 19 + .../searcher => search}/TreeSearchStatus.java | 2 +- .../searcher => search}/TreeSearcher.java | 2 +- .../search/control/TreeSearchCallback.java | 2 +- .../search/control/TreeSearchResult.java | 9 +- .../search/control/TreeSearchTask.java | 2 +- .../event/TreeSearchStatusChangeEvent.java | 4 +- .../event/TreeSearchStatusChangeListener.java | 2 +- .../search/view/TreeSearchRendererHelper.java | 16 +- .../java/com/fr/file/FILEChooserPane.java | 19 +- .../FileDealerPaneIcon/collapse-all.png | Bin 0 -> 177 bytes .../images/FileDealerPaneIcon/locate.png | Bin 0 -> 335 bytes .../com/fr/design/images/m_edit/move.png | Bin 0 -> 328 bytes 53 files changed, 3707 insertions(+), 391 deletions(-) create mode 100644 designer-base/src/main/java/com/fr/design/actions/file/DelFileAction.java create mode 100644 designer-base/src/main/java/com/fr/design/actions/file/LocateAction.java create mode 100644 designer-base/src/main/java/com/fr/design/actions/file/RenameAction.java create mode 100644 designer-base/src/main/java/com/fr/design/file/DefaultTemplateTreeDefineProcessor.java create mode 100644 designer-base/src/main/java/com/fr/design/file/TemplateDirTreePane.java create mode 100644 designer-base/src/main/java/com/fr/design/gui/itree/filetree/TemplateDirTree.java create mode 100644 designer-base/src/main/java/com/fr/design/mainframe/manager/clip/TemplateTreeClipboard.java create mode 100644 designer-base/src/main/java/com/fr/design/mainframe/manager/search/TemplateDirTreeSearchManager.java create mode 100644 designer-base/src/main/java/com/fr/design/mainframe/manager/search/TemplateTreeSearchManager.java create mode 100644 designer-base/src/main/java/com/fr/design/mainframe/manager/search/searcher/TemplateDirTreeSearcher.java create mode 100644 designer-base/src/main/java/com/fr/design/mainframe/manager/search/searcher/TemplateTreeSearcher.java create mode 100644 designer-base/src/main/java/com/fr/design/mainframe/manager/search/searcher/control/common/TemplateDirSearchCallBack.java create mode 100644 designer-base/src/main/java/com/fr/design/mainframe/manager/search/searcher/control/common/TemplateSearchCallBack.java create mode 100644 designer-base/src/main/java/com/fr/design/mainframe/manager/search/searcher/control/common/TemplateSearchResult.java create mode 100644 designer-base/src/main/java/com/fr/design/mainframe/manager/search/searcher/control/common/TemplateSearchTask.java create mode 100644 designer-base/src/main/java/com/fr/design/mainframe/manager/search/searcher/control/pane/TemplateDirSearchRemindPane.java create mode 100644 designer-base/src/main/java/com/fr/design/mainframe/manager/search/searcher/control/pane/TemplateDirTreeSearchPane.java create mode 100644 designer-base/src/main/java/com/fr/design/mainframe/manager/search/searcher/control/pane/TemplateSearchRemindPane.java create mode 100644 designer-base/src/main/java/com/fr/design/mainframe/manager/search/searcher/control/pane/TemplateTreeSearchToolbarPane.java create mode 100644 designer-base/src/main/java/com/fr/design/mainframe/manager/search/searcher/control/pre/TemplateDirPreSearchTask.java create mode 100644 designer-base/src/main/java/com/fr/design/mainframe/manager/search/searcher/control/pre/TemplatePreSearchCallBack.java create mode 100644 designer-base/src/main/java/com/fr/design/mainframe/manager/search/searcher/control/pre/TemplatePreSearchTask.java create mode 100644 designer-base/src/main/java/com/fr/design/search/TreeSearchManager.java rename designer-base/src/main/java/com/fr/design/{data/datapane/management/search/searcher => search}/TreeSearchStatus.java (84%) rename designer-base/src/main/java/com/fr/design/{data/datapane/management/search/searcher => search}/TreeSearcher.java (83%) rename designer-base/src/main/java/com/fr/design/{data/datapane/management => }/search/control/TreeSearchCallback.java (69%) rename designer-base/src/main/java/com/fr/design/{data/datapane/management => }/search/control/TreeSearchResult.java (57%) rename designer-base/src/main/java/com/fr/design/{data/datapane/management => }/search/control/TreeSearchTask.java (63%) rename designer-base/src/main/java/com/fr/design/{data/datapane/management => }/search/event/TreeSearchStatusChangeEvent.java (72%) rename designer-base/src/main/java/com/fr/design/{data/datapane/management => }/search/event/TreeSearchStatusChangeListener.java (76%) rename designer-base/src/main/java/com/fr/design/{data/datapane/management => }/search/view/TreeSearchRendererHelper.java (81%) create mode 100644 designer-base/src/main/resources/com/fr/design/images/FileDealerPaneIcon/collapse-all.png create mode 100644 designer-base/src/main/resources/com/fr/design/images/FileDealerPaneIcon/locate.png create mode 100644 designer-base/src/main/resources/com/fr/design/images/m_edit/move.png diff --git a/designer-base/src/main/java/com/fr/design/actions/file/DelFileAction.java b/designer-base/src/main/java/com/fr/design/actions/file/DelFileAction.java new file mode 100644 index 000000000..343167697 --- /dev/null +++ b/designer-base/src/main/java/com/fr/design/actions/file/DelFileAction.java @@ -0,0 +1,39 @@ +package com.fr.design.actions.file; + +import com.fr.design.actions.UpdateAction; +import com.fr.design.dialog.FineJOptionPane; +import com.fr.design.file.FileOperations; +import com.fr.design.i18n.Toolkit; +import com.fr.design.mainframe.DesignerContext; +import com.fr.design.mainframe.DesignerFrameFileDealerPane; + +import java.awt.event.ActionEvent; + +import static javax.swing.JOptionPane.WARNING_MESSAGE; + +/* + * 删除指定文件 + */ +public class DelFileAction extends UpdateAction { + + public DelFileAction() { + + this.setName(Toolkit.i18nText("Fine-Design_Basic_Remove")); + this.setSmallIcon("/com/fr/design/images/FileDealerPaneIcon/remove"); + } + + @Override + public void actionPerformed(ActionEvent evt) { + FileOperations selectedOperation = DesignerFrameFileDealerPane.getInstance().getSelectedOperation(); + if (!selectedOperation.access()) { + FineJOptionPane.showMessageDialog(DesignerContext.getDesignerFrame(), + Toolkit.i18nText("Fine-Design_Basic_Template_Permission_Denied"), + Toolkit.i18nText("Fine-Design_Basic_Alert"), + WARNING_MESSAGE); + return; + } + selectedOperation.deleteFile(); + DesignerFrameFileDealerPane.getInstance().stateChange(); + DesignerContext.getDesignerFrame().setTitle(); + } +} diff --git a/designer-base/src/main/java/com/fr/design/actions/file/LocateAction.java b/designer-base/src/main/java/com/fr/design/actions/file/LocateAction.java new file mode 100644 index 000000000..f079059e4 --- /dev/null +++ b/designer-base/src/main/java/com/fr/design/actions/file/LocateAction.java @@ -0,0 +1,136 @@ +package com.fr.design.actions.file; + +import com.fr.design.actions.UpdateAction; +import com.fr.design.file.HistoryTemplateListCache; +import com.fr.design.file.TemplateTreePane; +import com.fr.design.gui.itree.refreshabletree.ExpandMutableTreeNode; +import com.fr.design.gui.itree.refreshabletree.RefreshableJTree; +import com.fr.design.i18n.Toolkit; +import com.fr.design.mainframe.JTemplate; +import com.fr.file.filetree.FileNode; +import com.fr.general.ComparatorUtils; +import com.fr.stable.CoreConstants; +import com.fr.stable.StableUtils; +import com.fr.stable.project.ProjectConstants; + +import javax.swing.tree.DefaultTreeModel; +import javax.swing.tree.TreeNode; +import javax.swing.tree.TreePath; +import java.awt.event.ActionEvent; +import java.io.File; + +public class LocateAction extends UpdateAction { + + public LocateAction() { + this.setName(Toolkit.i18nText("Fine-Design_Basic_Locate")); + this.setSmallIcon("/com/fr/design/images/FileDealerPaneIcon/locate.png"); + } + + @Override + public void actionPerformed(ActionEvent e) { + JTemplate current = HistoryTemplateListCache.getInstance().getCurrentEditingTemplate(); + gotoEditingTemplateLeaf(current.getEditingFILE().getPath()); + } + + public static void gotoEditingTemplateLeaf(String locatedPath) { + if (locatedPath == null) { + return; + } + + DefaultTreeModel model = (DefaultTreeModel) TemplateTreePane.getInstance().getTemplateFileTree().getModel(); + ExpandMutableTreeNode treeNode = (ExpandMutableTreeNode) model.getRoot(); + + recursiveSelectPath(treeNode, locatedPath, model); + TreePath selectedTreePath = TemplateTreePane.getInstance().getTemplateFileTree().getSelectionPath(); + if (selectedTreePath != null) { + TemplateTreePane.getInstance().getTemplateFileTree().scrollPathToVisible(selectedTreePath); + } + } + + private static void recursiveSelectPath(TreeNode treeNode, String currentPath, DefaultTreeModel m_model) { + for (int i = 0, len = treeNode.getChildCount(); i < len; i++) { + TreeNode node = treeNode.getChildAt(i); + // 取出当前的childTreeNode,并append到searchingPath后面 + ExpandMutableTreeNode childTreeNode = (ExpandMutableTreeNode) node; + if (selectFilePath(childTreeNode, ProjectConstants.REPORTLETS_NAME, currentPath, m_model)) { + break; + } + if (!node.isLeaf()) { + for (int j = 0; j < node.getChildCount(); j++) { + recursiveSelectPath(node.getChildAt(j), currentPath, m_model); + } + } + } + } + + /* + * 在currentTreeNode下找寻filePath + * + * prefix + currentTreeNode.getName() = currentTreeNode所对应的Path + * + * 返回currentTreeNode下是否找到了filePath + */ + private static boolean selectFilePath(ExpandMutableTreeNode currentTreeNode, String prefix, String filePath, DefaultTreeModel model) { + Object userObj = currentTreeNode.getUserObject(); + if (!(userObj instanceof FileNode)) { + return false; + } + FileNode fileNode = (FileNode) userObj; + String nodePath = fileNode.getName(); + String currentTreePath = StableUtils.pathJoin(prefix, nodePath); + boolean result = false; + + // 如果equals,说明找到了,不必再找下去了 + if (ComparatorUtils.equals(new File(currentTreePath), new File(filePath))) { + TemplateTreePane.getInstance().getTemplateFileTree().setSelectionPath(new TreePath(model.getPathToRoot(currentTreeNode))); + result = true; + } + // 如果当前路径是currentFilePath的ParentFile,则expandTreeNode,并继续往下找 + else if (isParentFile(currentTreePath, filePath)) { + loadPendingChildTreeNode(currentTreeNode); + prefix = currentTreePath + CoreConstants.SEPARATOR; + for (int i = 0, len = currentTreeNode.getChildCount(); i < len; i++) { + ExpandMutableTreeNode childTreeNode = (ExpandMutableTreeNode) currentTreeNode.getChildAt(i); + + if (selectFilePath(childTreeNode, prefix, filePath, model)) { + result = true; + } + } + } + + return result; + } + + protected static void loadPendingChildTreeNode(ExpandMutableTreeNode currentTreeNode) { + if (currentTreeNode.isLeaf()) { + return; + } + + // 判断第一个孩子节点.UserObject是不是PENDING,如果是PENDING的话,需要重新加载这个TreeNode + ExpandMutableTreeNode flag = (ExpandMutableTreeNode) currentTreeNode.getFirstChild(); + if (flag == null || !flag.getUserObject().toString().equals(RefreshableJTree.PENDING.toString())) { + return; + } + // 删除所有的节点. + currentTreeNode.removeAllChildren(); + + ExpandMutableTreeNode[] children = TemplateTreePane.getInstance().getTemplateFileTree().loadChildTreeNodes(currentTreeNode); + for (ExpandMutableTreeNode c : children) { + currentTreeNode.add(c); + } + } + + private static boolean isParentFile(String var0, String var1) { + File var2 = new File(var0); + File var3 = new File(var1); + + while (!ComparatorUtils.equals(var2, var3)) { + var3 = var3.getParentFile(); + if (var3 == null) { + return false; + } + } + + return true; + } +} diff --git a/designer-base/src/main/java/com/fr/design/actions/file/RenameAction.java b/designer-base/src/main/java/com/fr/design/actions/file/RenameAction.java new file mode 100644 index 000000000..58e3cff68 --- /dev/null +++ b/designer-base/src/main/java/com/fr/design/actions/file/RenameAction.java @@ -0,0 +1,323 @@ +package com.fr.design.actions.file; + +import com.fr.base.BaseUtils; +import com.fr.chartx.TwoTuple; +import com.fr.design.DesignerEnvManager; +import com.fr.design.actions.UpdateAction; +import com.fr.design.dialog.FineJOptionPane; +import com.fr.design.file.FileOperations; +import com.fr.design.file.HistoryTemplateListCache; +import com.fr.design.file.MutilTempalteTabPane; +import com.fr.design.gui.ibutton.UIButton; +import com.fr.design.gui.ilable.UILabel; +import com.fr.design.gui.itextfield.UITextField; +import com.fr.design.i18n.Toolkit; +import com.fr.design.layout.TableLayout; +import com.fr.design.layout.TableLayoutHelper; +import com.fr.design.mainframe.DesignerContext; +import com.fr.design.mainframe.DesignerFrameFileDealerPane; +import com.fr.design.utils.gui.GUICoreUtils; +import com.fr.event.Event; +import com.fr.event.EventDispatcher; +import com.fr.file.FileNodeFILE; +import com.fr.file.filetree.FileNode; +import com.fr.general.ComparatorUtils; +import com.fr.stable.CoreConstants; +import com.fr.stable.StringUtils; +import com.fr.third.org.apache.commons.io.FilenameUtils; + +import javax.swing.BorderFactory; +import javax.swing.JDialog; +import javax.swing.JPanel; +import javax.swing.SwingConstants; +import javax.swing.event.DocumentEvent; +import javax.swing.event.DocumentListener; +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.Component; +import java.awt.Dimension; +import java.awt.FlowLayout; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.KeyAdapter; +import java.awt.event.KeyEvent; +import java.awt.event.KeyListener; +import java.util.regex.Pattern; + +import static javax.swing.JOptionPane.DEFAULT_OPTION; +import static javax.swing.JOptionPane.ERROR_MESSAGE; +import static javax.swing.JOptionPane.WARNING_MESSAGE; + + +public class RenameAction extends UpdateAction { + + private FileOperations selectedOperation; + public static final com.fr.event.Event> TEMPLATE_RENAME = new Event>() { + }; + + public RenameAction() { + + this.setName(Toolkit.i18nText("Fine-Design_Basic_Rename")); + this.setSmallIcon("/com/fr/design/images/FileDealerPaneIcon/rename"); + } + + @Override + public void actionPerformed(ActionEvent evt) { + selectedOperation = DesignerFrameFileDealerPane.getInstance().getSelectedOperation(); + if (!selectedOperation.access()) { + FineJOptionPane.showMessageDialog(DesignerContext.getDesignerFrame(), + Toolkit.i18nText("Fine-Design_Basic_Template_Permission_Denied"), + Toolkit.i18nText("Fine-Design_Basic_Alert"), + WARNING_MESSAGE); + return; + } + + FileNode node = selectedOperation.getFileNode(); + String lock = node.getLock(); + if (lock != null && !lock.equals(node.getUserID())) { + // 提醒被锁定模板无法重命名 + FineJOptionPane.showMessageDialog(DesignerContext.getDesignerFrame(), + Toolkit.i18nText("Fine-Design_Basic_Unable_Rename_Locked_File"), + Toolkit.i18nText("Fine-Design_Basic_Alert"), + WARNING_MESSAGE); + return; + } + + new FileRenameDialog(node); + MutilTempalteTabPane.getInstance().repaint(); + DesignerFrameFileDealerPane.getInstance().stateChange(); + } + + /** + * 重命名对话框 + * 支持快捷键Enter,ESC + */ + private class FileRenameDialog extends JDialog { + + private UITextField nameField; + + private UILabel warnLabel; + + private UIButton confirmButton; + + /** + * 操作的节点 + */ + private FileNodeFILE fnf; + + private KeyListener keyListener = new KeyAdapter() { + @Override + public void keyPressed(KeyEvent e) { + if (e.getKeyCode() == KeyEvent.VK_ESCAPE) { + dispose(); + } else if (e.getKeyCode() == KeyEvent.VK_ENTER) { + if (confirmButton.isEnabled()) { + confirmClose(); + } + } + } + }; + + + private FileRenameDialog(FileNode node) { + if (node == null) { + return; + } + fnf = new FileNodeFILE(node); + String oldName = fnf.getName(); + String suffix = fnf.isDirectory() ? StringUtils.EMPTY : oldName.substring(oldName.lastIndexOf(CoreConstants.DOT), oldName.length()); + oldName = StringUtils.replaceLast(oldName, suffix, StringUtils.EMPTY); + + initPane(oldName); + } + + private void initPane(String oldName) { + this.setLayout(new BorderLayout()); + this.setModal(true); + + // 输入框前提示 + UILabel newNameLabel = new UILabel(Toolkit.i18nText( + fnf.isDirectory() ? + "Fine-Design_Basic_Enter_New_Folder_Name" : "Fine-Design_Basic_Enter_New_File_Name") + ); + newNameLabel.setHorizontalAlignment(SwingConstants.RIGHT); + newNameLabel.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 10)); + //newNameLabel.setPreferredSize(new Dimension(118, 15)); + + // 重命名输入框 + nameField = new UITextField(oldName); + nameField.getDocument().addDocumentListener(new DocumentListener() { + + @Override + public void changedUpdate(DocumentEvent e) { + validInput(); + } + + @Override + public void insertUpdate(DocumentEvent e) { + validInput(); + } + + @Override + public void removeUpdate(DocumentEvent e) { + validInput(); + } + }); + nameField.selectAll(); + nameField.setPreferredSize(new Dimension(170, 20)); + + JPanel topPanel = new JPanel(new FlowLayout(FlowLayout.LEFT, 0, 5)); + topPanel.setBorder(BorderFactory.createEmptyBorder(15, 15, 0, 15)); + topPanel.add(newNameLabel); + topPanel.add(nameField); + + // 增加enter以及esc快捷键的支持 + nameField.addKeyListener(keyListener); + // 重名提示 + warnLabel = new UILabel(); + warnLabel.setPreferredSize(new Dimension(300, 50)); + warnLabel.setHorizontalAlignment(SwingConstants.LEFT); + warnLabel.setVerticalAlignment(SwingConstants.TOP); + warnLabel.setForeground(Color.RED); + warnLabel.setVisible(false); + + JPanel midPanel = new JPanel(new BorderLayout()); + midPanel.setBorder(BorderFactory.createEmptyBorder(0, 15, 0, 15)); + midPanel.add(warnLabel, BorderLayout.WEST); + + // 确认按钮 + confirmButton = new UIButton(Toolkit.i18nText("Fine-Design_Basic_Confirm")); + confirmButton.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + confirmClose(); + } + }); + + // 取消按钮 + UIButton cancelButton = new UIButton(Toolkit.i18nText("Fine-Design_Basic_Cancel")); + + cancelButton.addActionListener(new ActionListener() { + + @Override + public void actionPerformed(ActionEvent e) { + dispose(); + } + }); + + + JPanel buttonsPane = new JPanel(new FlowLayout(FlowLayout.RIGHT, 10, 0)); + buttonsPane.setBorder(BorderFactory.createEmptyBorder(10, 15, 10, 10)); + buttonsPane.add(confirmButton); + buttonsPane.add(cancelButton); + + this.add( + TableLayoutHelper.createTableLayoutPane( + new Component[][]{ + new Component[]{topPanel}, + new Component[]{midPanel}, + new Component[]{buttonsPane} + }, + new double[]{TableLayout.FILL, TableLayout.PREFERRED, TableLayout.PREFERRED}, + new double[]{TableLayout.FILL} + ), + BorderLayout.CENTER); + + this.setSize(340, 200); + this.setTitle(Toolkit.i18nText("Fine-Design_Basic_Rename")); + this.setResizable(false); + this.setAlwaysOnTop(true); + this.setIconImage(BaseUtils.readImage("/com/fr/base/images/oem/logo.png")); + this.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE); + GUICoreUtils.centerWindow(this); + this.setVisible(true); + } + + private void confirmClose() { + + String userInput = nameField.getText().trim(); + + String path = FilenameUtils.standard(fnf.getPath()); + + String oldName = fnf.getName(); + String suffix = fnf.isDirectory() ? StringUtils.EMPTY : oldName.substring(oldName.lastIndexOf(CoreConstants.DOT), oldName.length()); + oldName = StringUtils.replaceLast(oldName, suffix, StringUtils.EMPTY); + + // 输入为空或者没有修改 + if (ComparatorUtils.equals(userInput, oldName)) { + this.dispose(); + return; + } + + String parentPath = FilenameUtils.standard(fnf.getParent().getPath()); + + // 简单执行old new 替换是不可行的,例如 /abc/abc/abc/abc/ + String newPath = parentPath + CoreConstants.SEPARATOR + userInput + suffix; + this.dispose(); + + //模版重命名 + boolean success = selectedOperation.rename(fnf, path, newPath); + + if (success) { + EventDispatcher.fire(TEMPLATE_RENAME, new TwoTuple<>(path, newPath)); + HistoryTemplateListCache.getInstance().rename(fnf, path, newPath); + DesignerEnvManager.getEnvManager().replaceRecentOpenedFilePath(fnf.isDirectory(), path, newPath); + selectedOperation.refreshParent(); + DesignerContext.getDesignerFrame().setTitle(); + } else { + FineJOptionPane.showConfirmDialog(DesignerContext.getDesignerFrame(), + Toolkit.i18nText("Fine-Design_Basic_Rename_Failure"), + Toolkit.i18nText("Fine-Design_Basic_Error"), + DEFAULT_OPTION, + ERROR_MESSAGE); + } + } + + + private void validInput() { + + String userInput = nameField.getText().trim(); + + String oldName = fnf.getName(); + String suffix = fnf.isDirectory() ? StringUtils.EMPTY : oldName.substring(oldName.lastIndexOf(CoreConstants.DOT), oldName.length()); + oldName = oldName.replaceAll(suffix, StringUtils.EMPTY); + + if (StringUtils.isEmpty(userInput)) { + confirmButton.setEnabled(false); + return; + } + + if (ComparatorUtils.equals(userInput, oldName)) { + warnLabel.setVisible(false); + confirmButton.setEnabled(true); + return; + } + + String errorMsg = doCheck(userInput, suffix, fnf.isDirectory()); + if (StringUtils.isNotEmpty(errorMsg)) { + nameField.selectAll(); + // 如果文件名已存在,则灰掉确认按钮 + warnLabel.setText(errorMsg); + warnLabel.setVisible(true); + confirmButton.setEnabled(false); + } else { + warnLabel.setVisible(false); + confirmButton.setEnabled(true); + } + } + + private String doCheck (String userInput, String suffix, boolean isDirectory) { + String errorMsg = StringUtils.EMPTY; + if (selectedOperation.duplicated(userInput, suffix)) { + errorMsg = Toolkit.i18nText(isDirectory ? + "Fine-Design_Basic_Folder_Name_Duplicate" : + "Fine-Design_Basic_Template_File_Name_Duplicate", + userInput); + } + if (!Pattern.compile(DesignerFrameFileDealerPane.FILE_NAME_LIMIT).matcher(userInput).matches()) { + errorMsg = Toolkit.i18nText("Fine-Design_Basic_Template_Name_Illegal"); + } + return errorMsg; + } + } +} diff --git a/designer-base/src/main/java/com/fr/design/data/datapane/management/search/TableDataTreeSearchManager.java b/designer-base/src/main/java/com/fr/design/data/datapane/management/search/TableDataTreeSearchManager.java index f77a94db8..6d88a227d 100644 --- a/designer-base/src/main/java/com/fr/design/data/datapane/management/search/TableDataTreeSearchManager.java +++ b/designer-base/src/main/java/com/fr/design/data/datapane/management/search/TableDataTreeSearchManager.java @@ -4,12 +4,12 @@ import com.fr.data.TableDataSource; import com.fr.design.DesignModelAdapter; import com.fr.design.data.datapane.TableDataTree; import com.fr.design.data.datapane.TableDataTreePane; -import com.fr.design.data.datapane.management.search.event.TreeSearchStatusChangeEvent; -import com.fr.design.data.datapane.management.search.event.TreeSearchStatusChangeListener; +import com.fr.design.search.event.TreeSearchStatusChangeEvent; +import com.fr.design.search.event.TreeSearchStatusChangeListener; import com.fr.design.data.datapane.management.search.searcher.TableDataSearchMode; import com.fr.design.data.datapane.management.search.searcher.TableDataTreeSearcher; -import com.fr.design.data.datapane.management.search.searcher.TreeSearchStatus; -import com.fr.design.data.datapane.management.search.view.TreeSearchRendererHelper; +import com.fr.design.search.TreeSearchStatus; +import com.fr.design.search.view.TreeSearchRendererHelper; import com.fr.log.FineLoggerFactory; import com.fr.stable.StringUtils; @@ -101,7 +101,7 @@ public class TableDataTreeSearchManager { public void switchToSearch(TableDataSearchMode searchMode, TableDataSource tableDataSource) { setTreeSearchStatus(TreeSearchStatus.SEARCH_NOT_BEGIN); rendererHelper = new TreeSearchRendererHelper(); - rendererHelper.save(getCurrentTableDataTree()); + rendererHelper.save(getCurrentTableDataTree().getTableDataTreeCellRenderer()); treeSearcher = new TableDataTreeSearcher(); FineLoggerFactory.getLogger().debug("switch to table data search for mode: {}", searchMode.name()); treeSearcher.beforeSearch(searchMode, tableDataSource); diff --git a/designer-base/src/main/java/com/fr/design/data/datapane/management/search/control/common/TableDataSearchCallBack.java b/designer-base/src/main/java/com/fr/design/data/datapane/management/search/control/common/TableDataSearchCallBack.java index 6137032f0..d98b37c50 100644 --- a/designer-base/src/main/java/com/fr/design/data/datapane/management/search/control/common/TableDataSearchCallBack.java +++ b/designer-base/src/main/java/com/fr/design/data/datapane/management/search/control/common/TableDataSearchCallBack.java @@ -1,10 +1,10 @@ package com.fr.design.data.datapane.management.search.control.common; import com.fr.design.data.datapane.management.search.TableDataTreeSearchManager; -import com.fr.design.data.datapane.management.search.control.TreeSearchCallback; -import com.fr.design.data.datapane.management.search.control.TreeSearchResult; +import com.fr.design.search.control.TreeSearchCallback; +import com.fr.design.search.control.TreeSearchResult; import com.fr.design.data.datapane.management.search.searcher.TableDataTreeSearcher; -import com.fr.design.data.datapane.management.search.searcher.TreeSearchStatus; +import com.fr.design.search.TreeSearchStatus; import javax.swing.SwingUtilities; diff --git a/designer-base/src/main/java/com/fr/design/data/datapane/management/search/control/common/TableDataSearchResult.java b/designer-base/src/main/java/com/fr/design/data/datapane/management/search/control/common/TableDataSearchResult.java index 387933df5..695ae11d0 100644 --- a/designer-base/src/main/java/com/fr/design/data/datapane/management/search/control/common/TableDataSearchResult.java +++ b/designer-base/src/main/java/com/fr/design/data/datapane/management/search/control/common/TableDataSearchResult.java @@ -1,6 +1,6 @@ package com.fr.design.data.datapane.management.search.control.common; -import com.fr.design.data.datapane.management.search.control.TreeSearchResult; +import com.fr.design.search.control.TreeSearchResult; import java.util.ArrayList; import java.util.List; diff --git a/designer-base/src/main/java/com/fr/design/data/datapane/management/search/control/common/TableDataSearchTask.java b/designer-base/src/main/java/com/fr/design/data/datapane/management/search/control/common/TableDataSearchTask.java index 1a8b607c2..c7c9fad1f 100644 --- a/designer-base/src/main/java/com/fr/design/data/datapane/management/search/control/common/TableDataSearchTask.java +++ b/designer-base/src/main/java/com/fr/design/data/datapane/management/search/control/common/TableDataSearchTask.java @@ -1,8 +1,8 @@ package com.fr.design.data.datapane.management.search.control.common; -import com.fr.design.data.datapane.management.search.control.TreeSearchCallback; -import com.fr.design.data.datapane.management.search.control.TreeSearchResult; -import com.fr.design.data.datapane.management.search.control.TreeSearchTask; +import com.fr.design.search.control.TreeSearchCallback; +import com.fr.design.search.control.TreeSearchResult; +import com.fr.design.search.control.TreeSearchTask; import com.fr.design.data.tabledata.wrapper.StoreProcedureDataWrapper; import com.fr.design.data.tabledata.wrapper.TableDataWrapper; import com.fr.log.FineLoggerFactory; diff --git a/designer-base/src/main/java/com/fr/design/data/datapane/management/search/control/pre/TableDataPreSearchCallBack.java b/designer-base/src/main/java/com/fr/design/data/datapane/management/search/control/pre/TableDataPreSearchCallBack.java index 929c81a12..9c5dd8341 100644 --- a/designer-base/src/main/java/com/fr/design/data/datapane/management/search/control/pre/TableDataPreSearchCallBack.java +++ b/designer-base/src/main/java/com/fr/design/data/datapane/management/search/control/pre/TableDataPreSearchCallBack.java @@ -1,6 +1,6 @@ package com.fr.design.data.datapane.management.search.control.pre; -import com.fr.design.data.datapane.management.search.control.TreeSearchResult; +import com.fr.design.search.control.TreeSearchResult; import com.fr.design.data.datapane.management.search.control.common.TableDataSearchCallBack; import com.fr.design.data.datapane.management.search.searcher.TableDataTreeSearcher; diff --git a/designer-base/src/main/java/com/fr/design/data/datapane/management/search/control/pre/TableDataPreSearchResult.java b/designer-base/src/main/java/com/fr/design/data/datapane/management/search/control/pre/TableDataPreSearchResult.java index 6ebdb33ab..b13657452 100644 --- a/designer-base/src/main/java/com/fr/design/data/datapane/management/search/control/pre/TableDataPreSearchResult.java +++ b/designer-base/src/main/java/com/fr/design/data/datapane/management/search/control/pre/TableDataPreSearchResult.java @@ -1,6 +1,6 @@ package com.fr.design.data.datapane.management.search.control.pre; -import com.fr.design.data.datapane.management.search.control.TreeSearchResult; +import com.fr.design.search.control.TreeSearchResult; import java.util.ArrayList; import java.util.List; diff --git a/designer-base/src/main/java/com/fr/design/data/datapane/management/search/control/pre/TableDataPreSearchTask.java b/designer-base/src/main/java/com/fr/design/data/datapane/management/search/control/pre/TableDataPreSearchTask.java index 979eef1b0..2eb572fcb 100644 --- a/designer-base/src/main/java/com/fr/design/data/datapane/management/search/control/pre/TableDataPreSearchTask.java +++ b/designer-base/src/main/java/com/fr/design/data/datapane/management/search/control/pre/TableDataPreSearchTask.java @@ -1,11 +1,9 @@ package com.fr.design.data.datapane.management.search.control.pre; -import com.fr.design.data.datapane.management.search.TableDataTreeSearchManager; -import com.fr.design.data.datapane.management.search.control.TreeSearchTask; +import com.fr.design.search.control.TreeSearchTask; import com.fr.design.data.datapane.management.search.control.common.TableDataSearchResult; -import com.fr.design.data.datapane.management.search.control.TreeSearchCallback; -import com.fr.design.data.datapane.management.search.control.TreeSearchResult; -import com.fr.design.data.datapane.management.search.searcher.TreeSearchStatus; +import com.fr.design.search.control.TreeSearchCallback; +import com.fr.design.search.control.TreeSearchResult; import com.fr.design.data.tabledata.wrapper.TableDataWrapper; import com.fr.log.FineLoggerFactory; diff --git a/designer-base/src/main/java/com/fr/design/data/datapane/management/search/pane/TableDataSearchRemindPane.java b/designer-base/src/main/java/com/fr/design/data/datapane/management/search/pane/TableDataSearchRemindPane.java index 01b767623..dad2d4d4c 100644 --- a/designer-base/src/main/java/com/fr/design/data/datapane/management/search/pane/TableDataSearchRemindPane.java +++ b/designer-base/src/main/java/com/fr/design/data/datapane/management/search/pane/TableDataSearchRemindPane.java @@ -4,13 +4,13 @@ import com.fr.base.svg.IconUtils; import com.fr.design.constants.UIConstants; import com.fr.design.data.datapane.TableDataTree; import com.fr.design.data.datapane.management.search.TableDataTreeSearchManager; -import com.fr.design.data.datapane.management.search.event.TreeSearchStatusChangeEvent; -import com.fr.design.data.datapane.management.search.event.TreeSearchStatusChangeListener; -import com.fr.design.data.datapane.management.search.searcher.TreeSearchStatus; import com.fr.design.gui.icontainer.UIScrollPane; import com.fr.design.gui.ilable.UILabel; import com.fr.design.i18n.Toolkit; import com.fr.design.layout.FRGUIPaneFactory; +import com.fr.design.search.TreeSearchStatus; +import com.fr.design.search.event.TreeSearchStatusChangeEvent; +import com.fr.design.search.event.TreeSearchStatusChangeListener; import javax.swing.BorderFactory; import javax.swing.JPanel; diff --git a/designer-base/src/main/java/com/fr/design/data/datapane/management/search/pane/TreeSearchToolbarPane.java b/designer-base/src/main/java/com/fr/design/data/datapane/management/search/pane/TreeSearchToolbarPane.java index 7524efed2..6752fc1f1 100644 --- a/designer-base/src/main/java/com/fr/design/data/datapane/management/search/pane/TreeSearchToolbarPane.java +++ b/designer-base/src/main/java/com/fr/design/data/datapane/management/search/pane/TreeSearchToolbarPane.java @@ -5,9 +5,9 @@ import com.fr.design.DesignModelAdapter; import com.fr.design.constants.UIConstants; import com.fr.design.data.datapane.TableDataTreePane; import com.fr.design.data.datapane.management.search.TableDataTreeSearchManager; -import com.fr.design.data.datapane.management.search.event.TreeSearchStatusChangeEvent; -import com.fr.design.data.datapane.management.search.event.TreeSearchStatusChangeListener; -import com.fr.design.data.datapane.management.search.searcher.TreeSearchStatus; +import com.fr.design.search.event.TreeSearchStatusChangeEvent; +import com.fr.design.search.event.TreeSearchStatusChangeListener; +import com.fr.design.search.TreeSearchStatus; import com.fr.design.gui.ilable.UILabel; import com.fr.design.gui.itextfield.UITextField; import com.fr.design.gui.itoolbar.UIToolbar; @@ -16,7 +16,6 @@ import com.fr.design.layout.FRGUIPaneFactory; import com.fr.stable.StringUtils; import javax.swing.BorderFactory; -import javax.swing.JFrame; import javax.swing.JPanel; import javax.swing.event.DocumentEvent; import javax.swing.event.DocumentListener; diff --git a/designer-base/src/main/java/com/fr/design/data/datapane/management/search/searcher/TableDataTreeSearcher.java b/designer-base/src/main/java/com/fr/design/data/datapane/management/search/searcher/TableDataTreeSearcher.java index 672ef1e38..0b65d5481 100644 --- a/designer-base/src/main/java/com/fr/design/data/datapane/management/search/searcher/TableDataTreeSearcher.java +++ b/designer-base/src/main/java/com/fr/design/data/datapane/management/search/searcher/TableDataTreeSearcher.java @@ -9,6 +9,8 @@ import com.fr.design.data.datapane.management.search.control.common.TableDataSea import com.fr.design.data.datapane.management.search.control.pre.TableDataPreSearchCallBack; import com.fr.design.data.datapane.management.search.control.pre.TableDataPreSearchTask; import com.fr.design.data.tabledata.wrapper.TableDataWrapper; +import com.fr.design.search.TreeSearchStatus; +import com.fr.design.search.TreeSearcher; import java.util.HashSet; import java.util.List; diff --git a/designer-base/src/main/java/com/fr/design/file/DefaultTemplateTreeDefineProcessor.java b/designer-base/src/main/java/com/fr/design/file/DefaultTemplateTreeDefineProcessor.java new file mode 100644 index 000000000..a01ca0c7a --- /dev/null +++ b/designer-base/src/main/java/com/fr/design/file/DefaultTemplateTreeDefineProcessor.java @@ -0,0 +1,545 @@ +package com.fr.design.file; + +import com.fr.design.actions.UpdateAction; +import com.fr.design.actions.file.DelFileAction; +import com.fr.design.actions.file.LocateAction; +import com.fr.design.actions.file.RenameAction; +import com.fr.design.dialog.FineJOptionPane; +import com.fr.design.fun.impl.AbstractTemplateTreeDefineProcessor; +import com.fr.design.gui.ibutton.UIButton; +import com.fr.design.gui.imenu.UIPopupMenu; +import com.fr.design.gui.itree.filetree.TemplateDirTree; +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.mainframe.DesignerContext; +import com.fr.design.mainframe.DesignerFrameFileDealerPane; +import com.fr.design.mainframe.manager.clip.TemplateTreeClipboard; +import com.fr.design.mainframe.manager.search.TemplateDirTreeSearchManager; +import com.fr.design.mainframe.manager.search.TemplateTreeSearchManager; +import com.fr.design.mainframe.manager.search.searcher.control.pane.TemplateDirTreeSearchPane; +import com.fr.design.utils.gui.GUICoreUtils; +import com.fr.file.FileNodeFILE; +import com.fr.file.filetree.FileNode; +import com.fr.general.ComparatorUtils; +import com.fr.log.FineLoggerFactory; +import com.fr.stable.StableUtils; +import com.fr.stable.StringUtils; +import com.fr.stable.collections.CollectionUtils; +import com.fr.stable.project.ProjectConstants; +import com.fr.workspace.WorkContext; + +import javax.swing.BorderFactory; +import javax.swing.JDialog; +import javax.swing.JOptionPane; +import javax.swing.JPanel; +import javax.swing.SwingUtilities; +import java.awt.BorderLayout; +import java.awt.CardLayout; +import java.awt.Dimension; +import java.awt.FlowLayout; +import java.awt.HeadlessException; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.MouseEvent; +import java.util.ArrayList; +import java.util.List; + +import static javax.swing.JOptionPane.INFORMATION_MESSAGE; +import static javax.swing.JOptionPane.WARNING_MESSAGE; +import static javax.swing.JOptionPane.YES_NO_OPTION; + +public class DefaultTemplateTreeDefineProcessor extends AbstractTemplateTreeDefineProcessor { + + private UIPopupMenu popupMenu; + private RenameAction renameAction; + private CopyAction copyAction; + private PasteAction pasteAction; + private DelFileAction delFileAction; + private MoveAction moveAction; + + public static DefaultTemplateTreeDefineProcessor getInstance() { + return DefaultTemplateTreeDefineProcessor.HOLDER.singleton; + } + + private static class HOLDER { + private static DefaultTemplateTreeDefineProcessor singleton = new DefaultTemplateTreeDefineProcessor(); + } + + private DefaultTemplateTreeDefineProcessor() { + initPane(); + } + + private void initPane() { + renameAction = new RenameAction(); + copyAction = new CopyAction(); + pasteAction = new PasteAction(); + delFileAction = new DelFileAction(); + moveAction = new MoveAction(); + //右键菜单 + popupMenu = new UIPopupMenu(); + popupMenu.add(renameAction.createMenuItem()); + popupMenu.addSeparator(); + popupMenu.add(copyAction.createMenuItem()); + popupMenu.add(pasteAction.createMenuItem()); + popupMenu.add(delFileAction.createMenuItem()); + popupMenu.addSeparator(); + popupMenu.add(moveAction.createMenuItem()); + } + + @Override + public void rightClickAction(MouseEvent e) { + if (SwingUtilities.isRightMouseButton(e)) { + GUICoreUtils.showPopupMenu(popupMenu, e.getComponent(), e.getX(), e.getY()); + checkButtonEnabled(); + } + } + + private void checkButtonEnabled() { + renameAction.setEnabled(false); + copyAction.setEnabled(false); + pasteAction.setEnabled(false); + delFileAction.setEnabled(false); + moveAction.setEnabled(false); + int length = getFileTree().getSelectionCount(); + if (length == 0) { + //没有选中文件时,只能黏贴 + if (!CollectionUtils.isEmpty(TemplateTreeClipboard.getInstance().takeFromClip())) { + pasteAction.setEnabled(true); + } + return; + } + if (length == 1) { + //选中一个时可以,可以重命名、黏贴 + renameAction.setEnabled(true); + if (!CollectionUtils.isEmpty(TemplateTreeClipboard.getInstance().takeFromClip())) { + pasteAction.setEnabled(true); + } + } + moveAction.setEnabled(true); + delFileAction.setEnabled(true); + copyAction.setEnabled(true); + } + + private TemplateFileTree getFileTree() { + return TemplateTreePane.getInstance().getTemplateFileTree(); + } + + private TemplateDirTree getDirTree() { + return TemplateDirTreePane.getInstance().getTemplateDirTree(); + } + + private class CopyAction extends UpdateAction { + + public CopyAction() { + this.setName(Toolkit.i18nText("Fine-Design_Basic_Copy")); + this.setMnemonic('C'); + this.setSmallIcon("/com/fr/design/images/m_edit/copy"); + } + + @Override + public void actionPerformed(ActionEvent e) { + FileOperations selectedOperation = DesignerFrameFileDealerPane.getInstance().getSelectedOperation(); + if (!selectedOperation.access()) { + FineJOptionPane.showMessageDialog(DesignerContext.getDesignerFrame(), + Toolkit.i18nText("Fine-Design_Basic_Template_Permission_Denied"), + Toolkit.i18nText("Fine-Design_Basic_Alert"), + WARNING_MESSAGE); + return; + } + //先记录所有的要复制的模板 + List treeNodeList = TemplateTreeClipboard.getInstance().transferNameObjectArray2Map(getFileTree().getSelectedTreeNodes()); + TemplateTreeClipboard.getInstance().addToClip(treeNodeList); + } + } + + private class PasteAction extends UpdateAction { + + public PasteAction() { + this.setName(Toolkit.i18nText("Fine-Design_Basic_Action_Paste_Name")); + this.setMnemonic('P'); + this.setSmallIcon("/com/fr/design/images/m_edit/paste"); + } + + @Override + public void actionPerformed(ActionEvent e) { + List treeNodeList = TemplateTreeClipboard.getInstance().takeFromClip(); + if (!canPaste(treeNodeList)) { + return; + } + String targetDir = getTargetDir(); + + // 筛选可以黏贴的文件 + ArrayList pasteNodes = new ArrayList<>(); + ArrayList lockedNodes = new ArrayList<>(); + for (ExpandMutableTreeNode treeNode : treeNodeList) { + checkFreeOrLock(treeNode, pasteNodes, lockedNodes); + } + if (lockedNodes.isEmpty()) { + doPaste(targetDir, pasteNodes); + } else { + if (FineJOptionPane.showConfirmDialog(DesignerContext.getDesignerFrame(), + Toolkit.i18nText("Fine-Design_Basic_Confirm_Paste_Unlock_File"), + Toolkit.i18nText("Fine-Design_Basic_Confirm"), + YES_NO_OPTION) + == JOptionPane.YES_OPTION) { + // 黏贴其他可黏贴的文件 + doPaste(targetDir, pasteNodes); + } + } + + // 移动时如果正在搜索,跳回原树 + if (TemplateTreeSearchManager.getInstance().isInSearchMode()) { + TemplateTreeSearchManager.getInstance().outOfSearchMode(); + } + DesignerFrameFileDealerPane.getInstance().getSelectedOperation().refresh(); + String targetFile = StableUtils.pathJoin(targetDir, ((FileNode) (pasteNodes.get(0).getUserObject())).getName()); + LocateAction.gotoEditingTemplateLeaf(targetFile); + } + + /** + * 检测是否能够黏贴 + * @param treeNodeList + * @return + */ + private boolean canPaste(List treeNodeList) { + if (CollectionUtils.isEmpty(treeNodeList)) { + return false; + } + //确定目标目录并检查权限 + FileOperations selectedOperation = DesignerFrameFileDealerPane.getInstance().getSelectedOperation(); + if (getFileTree().getSelectionCount() != 0 && !selectedOperation.access()) { + FineJOptionPane.showMessageDialog(DesignerContext.getDesignerFrame(), + Toolkit.i18nText("Fine-Design_Basic_Template_Permission_Denied"), + Toolkit.i18nText("Fine-Design_Basic_Alert"), + WARNING_MESSAGE); + return false; + } + return true; + } + + + private void doPaste(String targetDir, List pasteNodes) { + try { + if (StringUtils.isEmpty(targetDir)) { + return; + } + if (pasteNodes.isEmpty()) { + //提示:复制的文件都不能黏贴 + FineJOptionPane.showMessageDialog(DesignerContext.getDesignerFrame(), + Toolkit.i18nText("Fine-Design_Basic_Unable_Delete_Locked_File"), + Toolkit.i18nText("Fine-Design_Basic_Alert"), + WARNING_MESSAGE); + return; + } + for (ExpandMutableTreeNode node : pasteNodes) { + if (node.getUserObject() instanceof FileNode) { + FileNode fileNode = (FileNode) node.getUserObject(); + copyFile(fileNode, targetDir); + FineLoggerFactory.getLogger().debug("Template: {} paste to {} success.", fileNode.getEnvPath(), targetDir); + } + } + } catch (HeadlessException e) { + FineLoggerFactory.getLogger().error(e,"Template paste failed.", e.getMessage()); + FineJOptionPane.showConfirmDialog(null, + Toolkit.i18nText("Fine-Design_Basic_Paste_Failure"), + Toolkit.i18nText("Fine-Design_Basic_Error"), + JOptionPane.DEFAULT_OPTION, + JOptionPane.ERROR_MESSAGE); + } + } + } + + + private class MoveAction extends UpdateAction { + + public MoveAction() { + this.setName(Toolkit.i18nText("Fine-Design_Basic_Move")); + this.setSmallIcon("/com/fr/design/images/m_edit/move"); + } + + @Override + public void actionPerformed(ActionEvent e) { + FileOperations selectedOperation = DesignerFrameFileDealerPane.getInstance().getSelectedOperation(); + if (!selectedOperation.access()) { + FineJOptionPane.showMessageDialog(DesignerContext.getDesignerFrame(), + Toolkit.i18nText("Fine-Design_Basic_Template_Permission_Denied"), + Toolkit.i18nText("Fine-Design_Basic_Alert"), + WARNING_MESSAGE); + return; + } + + FileNode node = selectedOperation.getFileNode(); + String lock = node.getLock(); + if (lock != null && !lock.equals(node.getUserID())) { + // 提醒被锁定模板无法移动 + FineJOptionPane.showMessageDialog(DesignerContext.getDesignerFrame(), + Toolkit.i18nText("Fine-Design_Basic_Unable_Move_Locked_File"), + Toolkit.i18nText("Fine-Design_Basic_Alert"), + WARNING_MESSAGE); + return; + } + + new TemplateMoveDialog(); + } + } + + private class TemplateMoveDialog extends JDialog { + + private static final String DIR = "dir"; + private TemplateDirTreeSearchPane searchPane; + private TemplateDirTreePane dirTreePane; + private UIButton confirmButton; + private String targetFile; + + public TemplateMoveDialog() { + this.setLayout(new BorderLayout()); + this.setModal(true); + + searchPane = new TemplateDirTreeSearchPane(); + add(searchPane, BorderLayout.NORTH); + + CardLayout card; + JPanel cardPane = new JPanel(card = new CardLayout()); + cardPane.add(TemplateDirTreePane.getInstance(), DIR); + dirTreePane = TemplateDirTreePane.getInstance(); + card.show(cardPane, DIR); + add(cardPane, BorderLayout.CENTER); + cardPane.setBorder(BorderFactory.createEmptyBorder(10, 15, 0, 10)); + dirTreePane.refresh(); + + // 确认按钮,默认就可用 + confirmButton = new UIButton(Toolkit.i18nText("Fine-Design_Basic_Confirm")); + confirmButton.setPreferredSize(new Dimension(60, 25)); + confirmButton.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + confirmClose(); + } + }); + confirmButton.setEnabled(true); + + // 取消按钮 + UIButton cancelButton = new UIButton(Toolkit.i18nText("Fine-Design_Basic_Cancel")); + cancelButton.setPreferredSize(new Dimension(60, 25)); + + cancelButton.addActionListener(new ActionListener() { + + @Override + public void actionPerformed(ActionEvent e) { + dispose(); + } + }); + + JPanel bottomPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT, 10, 0)); + bottomPanel.setBorder(BorderFactory.createEmptyBorder(10, 15, 10, 10)); + bottomPanel.add(confirmButton); + bottomPanel.add(cancelButton); + this.add(bottomPanel, BorderLayout.SOUTH); + + this.setSize(new Dimension(600, 400)); + this.setTitle(Toolkit.i18nText("Fine-Design_Basic_Move")); + this.setResizable(false); + this.setAlwaysOnTop(true); + this.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE); + GUICoreUtils.setWindowCenter(DesignerContext.getDesignerFrame(), this); + this.setVisible(true); + } + + private void confirmClose() { + //获取目录数中所选中的文件,并判断是否有权限 + if (getFileTree().getSelectionCount() != 0 && !TemplateDirTreePane.getInstance().selectedAccess()) { + FineJOptionPane.showMessageDialog(DesignerContext.getDesignerFrame(), + Toolkit.i18nText("Fine-Design_Basic_Template_Permission_Denied"), + Toolkit.i18nText("Fine-Design_Basic_Alert"), + WARNING_MESSAGE); + return; + } + boolean moveSuccess = doMove(); + dispose(); + if (moveSuccess) { + FineJOptionPane.showMessageDialog(DesignerContext.getDesignerFrame(), + Toolkit.i18nText("Fine-Design_Basic_Template_Moved_Success"), + Toolkit.i18nText("Fine-Design_Basic_Alert"), + INFORMATION_MESSAGE); + + // 移动时如果正在搜索,跳回原树 + if (TemplateTreeSearchManager.getInstance().isInSearchMode()) { + TemplateTreeSearchManager.getInstance().outOfSearchMode(); + } + DesignerFrameFileDealerPane.getInstance().getSelectedOperation().refresh(); + LocateAction.gotoEditingTemplateLeaf(targetFile); + } + } + + private boolean doMove() { + FileNode fileNode = getDirTree().getSelectedFileNode(); + fileNode = fileNode == null ? (FileNode) getFileTree().getModel().getRoot() : fileNode; + boolean moveSuccess = true; + try { + //待移动的文件可以有多个 + ExpandMutableTreeNode[] sourceSelected = getFileTree().getSelectedTreeNodes(); + for (ExpandMutableTreeNode treeNode : sourceSelected) { + FileNode sourceFileNode = (FileNode) treeNode.getUserObject(); + targetFile = copyFile(sourceFileNode, fileNode.getEnvPath()); + FileNodeFILE nodeFILE = new FileNodeFILE(sourceFileNode); + if (nodeFILE.exists()) { + if (TemplateResourceManager.getResource().delete(nodeFILE)) { + HistoryTemplateListCache.getInstance().deleteFile(nodeFILE); + FineLoggerFactory.getLogger().info("template {} move to {} success.", sourceFileNode.getEnvPath(), fileNode.getEnvPath()); + } else { + //删除失败,将复制过去的文件删掉 + TemplateResourceManager.getResource().delete(new FileNodeFILE(targetFile)); + FineLoggerFactory.getLogger().error("template {} move to {} failed.", sourceFileNode.getEnvPath(), fileNode.getEnvPath()); + moveSuccess = false; + } + } + } + } catch (Exception e) { + FineLoggerFactory.getLogger().error(e.getMessage(), e); + FineJOptionPane.showMessageDialog(this, + Toolkit.i18nText("Fine-Design_Basic_Template_Moved_Fail"), + Toolkit.i18nText("Fine-Design_Basic_Alert"), + WARNING_MESSAGE); + moveSuccess = false; + } + return moveSuccess; + } + + @Override + public void dispose() { + TemplateDirTreeSearchManager.getInstance().outOfSearchMode(); + super.dispose(); + } + } + + private boolean checkFreeOrLock(ExpandMutableTreeNode node, ArrayList dNodes, ArrayList lNodes) { + // 自己没锁 + boolean selfEmptyLock = false; + Object userObj = node.getUserObject(); + if (userObj instanceof FileNode) { + String lock = ((FileNode) userObj).getLock(); + selfEmptyLock = lock == null || ((FileNode) userObj).getUserID().equals(lock); + } + + if (node.isLeaf()) { + if (selfEmptyLock) { + dNodes.add(node); + } else { + lNodes.add(node); + } + return selfEmptyLock; + } + + return checkChildNode(node, dNodes, lNodes, selfEmptyLock); + } + + private boolean checkChildNode(ExpandMutableTreeNode node, ArrayList dNodes, ArrayList lNodes, boolean selfEmptyLock) { + ExpandMutableTreeNode[] children = getFileTree().loadChildTreeNodes(node); + + boolean childrenEmptyLock = true; + + for (ExpandMutableTreeNode child : children) { + childrenEmptyLock = checkFreeOrLock(child, dNodes, lNodes) && childrenEmptyLock; + } + + boolean emptyLock = childrenEmptyLock && selfEmptyLock; + if (emptyLock) { + dNodes.add(node); + } else { + lNodes.add(node); + } + return emptyLock; + } + + /** + * 黏贴时确定黏贴的目录 + * + * @return + */ + private String getTargetDir() { + int count = getFileTree().getSelectionCount(); + String targetDir; + if (count == 0) { + targetDir = ProjectConstants.REPORTLETS_NAME; + } else { + FileNode fileNode = getFileTree().getSelectedFileNode(); + targetDir = fileNode.getParent(); + if (fileNode.isDirectory()) { + targetDir = StableUtils.pathJoin(targetDir, fileNode.getName()); + } + } + return targetDir; + } + + private void copyDir(String sourceDir, String targetDir) { + FileNode[] fileNodes = getFileTree().listFile(sourceDir); + for (FileNode fileNode : fileNodes) { + if (fileNode.isDirectory()) { + copyDir(StableUtils.pathJoin(fileNode.getParent(), fileNode.getName()), StableUtils.pathJoin(targetDir, fileNode.getName())); + } + copyFile(StableUtils.pathJoin(sourceDir, fileNode.getName()), StableUtils.pathJoin(targetDir, fileNode.getName())); + } + } + + private void copyFile(String sourcePath, String targetPath) { + //检查源文件是不是还存在 + if (!WorkContext.getWorkResource().exist(sourcePath)) { + FineJOptionPane.showMessageDialog(DesignerContext.getDesignerFrame(), + Toolkit.i18nText("Fine-Design_Basic_Source_File_Not_Exist"), + Toolkit.i18nText("Fine-Design_Basic_Alert"), + WARNING_MESSAGE); + } else { + try { + byte[] data = WorkContext.getWorkResource().readFully(sourcePath); + WorkContext.getWorkResource().write(targetPath, data); + } catch (Exception e) { + FineLoggerFactory.getLogger().error(e.getMessage(), e); + throw e; + } + } + } + + private String copyFile(FileNode sourceFile, String targetDir) { + String name = getNoRepeatedName4Paste(targetDir, sourceFile.getName()); + String targetFile = StableUtils.pathJoin(targetDir, name); + if (sourceFile.isDirectory()) { + copyDir(sourceFile.getEnvPath(), targetFile); + } else { + copyFile(sourceFile.getEnvPath(), targetFile); + } + return targetFile; + } + + /** + * 重名处理 + * + * @param targetDir + * @param oldName + * @return + */ + private String getNoRepeatedName4Paste(String targetDir, String oldName) { + while (isNameRepeaded(targetDir, oldName)) { + int index = oldName.lastIndexOf("."); + if (index > 0) { + String oName = oldName.substring(0, index); + oName = oName + Toolkit.i18nText("Fine-Design_Table_Data_Copy_Of_Table_Data"); + oldName = oName.concat(oldName.substring(index)); + } else { + //目录重名 + oldName = oldName + Toolkit.i18nText("Fine-Design_Table_Data_Copy_Of_Table_Data"); + } + } + return oldName; + } + + private boolean isNameRepeaded(String targetDir, String name) { + FileNode[] fileNodes = getFileTree().listFile(targetDir); + for (int i = 0; i < fileNodes.length; i++) { + if (ComparatorUtils.equals(name, fileNodes[i].getName())) { + return true; + } + } + return false; + } +} diff --git a/designer-base/src/main/java/com/fr/design/file/FileOperations.java b/designer-base/src/main/java/com/fr/design/file/FileOperations.java index 6509142a4..578739373 100644 --- a/designer-base/src/main/java/com/fr/design/file/FileOperations.java +++ b/designer-base/src/main/java/com/fr/design/file/FileOperations.java @@ -29,6 +29,11 @@ public interface FileOperations { */ void refresh(); + /** + * 刷新父目录 + */ + void refreshParent(); + /** * 删除文件 */ diff --git a/designer-base/src/main/java/com/fr/design/file/MutilTempalteTabPane.java b/designer-base/src/main/java/com/fr/design/file/MutilTempalteTabPane.java index 1920212f4..1e25ddb3d 100644 --- a/designer-base/src/main/java/com/fr/design/file/MutilTempalteTabPane.java +++ b/designer-base/src/main/java/com/fr/design/file/MutilTempalteTabPane.java @@ -4,9 +4,12 @@ package com.fr.design.file; import com.fr.base.BaseUtils; import com.fr.base.GraphHelper; import com.fr.base.vcs.DesignerMode; +import com.fr.design.actions.UpdateAction; +import com.fr.design.actions.file.LocateAction; import com.fr.design.constants.UIConstants; import com.fr.design.dialog.FineJOptionPane; import com.fr.design.gui.imenu.UIMenuItem; +import com.fr.design.gui.imenu.UIPopupMenu; import com.fr.design.gui.imenu.UIScrollPopUpMenu; import com.fr.design.i18n.Toolkit; import com.fr.design.mainframe.DesignerContext; @@ -24,6 +27,8 @@ import com.fr.general.IOUtils; import com.fr.log.FineLoggerFactory; import com.fr.stable.Constants; import com.fr.third.javax.annotation.Nonnull; +import com.fr.workspace.WorkContext; +import com.fr.workspace.server.lock.TplOperator; import javax.swing.BorderFactory; import javax.swing.ButtonModel; @@ -35,7 +40,9 @@ import javax.swing.JMenuItem; import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JSeparator; +import javax.swing.MenuElement; import javax.swing.SwingConstants; +import javax.swing.SwingUtilities; import javax.swing.plaf.basic.BasicMenuItemUI; import java.awt.AWTEvent; import java.awt.AlphaComposite; @@ -49,6 +56,7 @@ import java.awt.RenderingHints; import java.awt.event.AWTEventListener; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; +import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.awt.event.MouseListener; import java.awt.event.MouseMotionListener; @@ -160,6 +168,161 @@ public class MutilTempalteTabPane extends JComponent { }; java.awt.Toolkit.getDefaultToolkit().addAWTEventListener(awt, AWTEvent.MOUSE_EVENT_MASK); + addMouseListener(new MouseAdapter() { + @Override + public void mousePressed(MouseEvent e) { + if (SwingUtilities.isRightMouseButton(e)) { + int tplIndex = getTemplateIndex(e.getX()); + if (tplIndex > -1) { + UIPopupMenu menu = new UIPopupMenu(); + menu.setBorder(BorderFactory.createEmptyBorder(-3, 3, 3, 0)); + + for (CloseOption option : CloseOption.values()) { + menu.add(new UIMenuItem(new RightMenuCloseAction(option, tplIndex))); + } + menu.add(new CloseMenuItemJSeparator()); + menu.add(new UIMenuItem(new OpenInTemplateTreeAction(tplIndex))); + + int height = 0; + for (MenuElement subElement : menu.getSubElements()) { + if (subElement instanceof CloseMenuItemJSeparator) { + height += 10; + } else { + height += 25; + } + } + menu.setPreferredSize(new Dimension(170, height)); + GUICoreUtils.showPopupMenu(menu, MutilTempalteTabPane.getInstance(), e.getX(), MutilTempalteTabPane.getInstance().getY() - 1 + MutilTempalteTabPane.getInstance().getHeight()); + } + } + } + }); + } + + enum CloseOption { + Left(Toolkit.i18nText("Fine-Design_Close_templates_To_The_Left")) { + @Override + boolean shouldClose(int tplIndex, int i) { + return i < tplIndex; + } + }, + Right(Toolkit.i18nText("Fine-Design_Close_templates_To_The_Right")) { + @Override + boolean shouldClose(int tplIndex, int i) { + return i > tplIndex; + } + }, + All(Toolkit.i18nText("Fine-Design_Close_All_templates")), + Others(Toolkit.i18nText("Fine-Design_Close_Other_templates")) { + @Override + boolean shouldClose(int tplIndex, int i) { + return i != tplIndex; + } + }; + + + String optionName; + + CloseOption(String optionName) { + this.optionName = optionName; + } + + boolean shouldClose(int tplIndex, int i) { + return true; + } + } + + private static class CloseMenuItemJSeparator extends JSeparator { + @Override + public Dimension getPreferredSize() { + Dimension d = super.getPreferredSize(); + d.height = 1; + return d; + } + + @Override + public Color getForeground() { + return UIConstants.PRESSED_DARK_GRAY; + } + } + + + private class OpenInTemplateTreeAction extends LocateAction { + + int tplIndex; + + public OpenInTemplateTreeAction(int tplIndex) { + this.tplIndex = tplIndex; + this.setName(Toolkit.i18nText("Fine-Design_Open_In_Template_Tree")); + } + + @Override + public void actionPerformed(ActionEvent e) { + JTemplate template = openedTemplate.get(this.tplIndex); + super.gotoEditingTemplateLeaf(template.getPath()); + } + } + + private class RightMenuCloseAction extends UpdateAction { + + CloseOption option; + int tplIndex = -1; + + public RightMenuCloseAction(CloseOption option, int tplIndex) { + this.option = option; + this.setName(option.optionName); + this.tplIndex = tplIndex; + } + + + @Override + public void actionPerformed(ActionEvent e) { + SaveSomeTemplatePane saveSomeTempaltePane = new SaveSomeTemplatePane(false); + if (saveSomeTempaltePane.showSavePane()) { + + JTemplate[] templates = new JTemplate[openedTemplate.size()]; + for (int i = 0; i < openedTemplate.size(); i++) { + templates[i] = openedTemplate.get(i); + } + JTemplate currentTemplate = HistoryTemplateListCache.getInstance().getCurrentEditingTemplate(); + closeTemplate(templates, currentTemplate); + + if (currentTemplate == null) { + DesignerContext.getDesignerFrame().addAndActivateJTemplate(); + } else { + DesignerContext.getDesignerFrame().activateJTemplate(currentTemplate); + } + + MutilTempalteTabPane.getInstance().repaint(); + } + } + + private void closeTemplate(JTemplate[] templates, JTemplate currentTemplate) { + for (int i = 0; i < templates.length; i++) { + if (option.shouldClose(tplIndex, i)) { + JTemplate jTemplate = templates[i]; + if (jTemplate == currentTemplate) { + currentTemplate = option == CloseOption.All ? null : templates[tplIndex]; + } + //判断关闭的模板是不是格式刷的被参照的模板 + openedTemplate.remove(jTemplate); + if (jTemplate != currentTemplate) { + MutilTempalteTabPane.getInstance().closeFormat(jTemplate); + HistoryTemplateListCache.getInstance().closeSelectedReport(jTemplate); + closeAndFreeLock(jTemplate); + } + } + } + } + + private void closeAndFreeLock(@Nonnull JTemplate template) { + FILE file = template.getEditingFILE(); + // 只有是环境内的文件,才执行释放锁 + if (file != null && file.isEnvFile()) { + // release lock + WorkContext.getCurrent().get(TplOperator.class).closeAndFreeFile(file.getPath()); + } + } } public JTemplate getSelectedFile() { diff --git a/designer-base/src/main/java/com/fr/design/file/TemplateDirTreePane.java b/designer-base/src/main/java/com/fr/design/file/TemplateDirTreePane.java new file mode 100644 index 000000000..58740393d --- /dev/null +++ b/designer-base/src/main/java/com/fr/design/file/TemplateDirTreePane.java @@ -0,0 +1,79 @@ +package com.fr.design.file; + +import com.fr.base.FRContext; +import com.fr.design.gui.itree.filetree.TemplateDirTree; +import com.fr.design.gui.itree.refreshabletree.ExpandMutableTreeNode; +import com.fr.design.i18n.Toolkit; +import com.fr.design.layout.FRGUIPaneFactory; +import com.fr.design.mainframe.DesignerFrameFileDealerPane; +import com.fr.design.mainframe.manager.search.searcher.control.pane.TemplateDirSearchRemindPane; +import com.fr.file.filetree.IOFileNodeFilter; +import com.fr.log.FineLoggerFactory; +import com.fr.stable.ArrayUtils; + +import javax.swing.BorderFactory; +import javax.swing.JPanel; +import javax.swing.tree.TreePath; +import java.awt.BorderLayout; +import java.awt.Color; + +public class TemplateDirTreePane extends JPanel { + + public static TemplateDirTreePane getInstance() { + return TemplateDirTreePane.HOLDER.singleton; + } + + private static class HOLDER { + private static TemplateDirTreePane singleton = new TemplateDirTreePane(); + } + + private TemplateDirTree templateDirTree; + private TemplateDirSearchRemindPane remindPane; + + public TemplateDirTreePane() { + this.setLayout(FRGUIPaneFactory.createBorderLayout()); + this.setBorder(BorderFactory.createLineBorder(Color.gray)); + templateDirTree = new TemplateDirTree(); + remindPane = new TemplateDirSearchRemindPane(getTemplateDirTree()); + + this.add(remindPane, BorderLayout.CENTER); + } + + public TemplateDirTree getTemplateDirTree() { + return this.templateDirTree; + } + + /** + * 判断所选的目录是否有权限 + * + * @return + */ + public boolean selectedAccess() { + TreePath[] selectedTreePaths = templateDirTree.getSelectionPaths(); + + if (ArrayUtils.isEmpty(selectedTreePaths)) { + return false; + } + // 选中的是文件夹 + TreePath treePath = selectedTreePaths[0]; + ExpandMutableTreeNode currentTreeNode = (ExpandMutableTreeNode) treePath.getLastPathComponent(); + return currentTreeNode != null && currentTreeNode.hasFullAuthority(); + } + + public void refresh() { + // 刷新远程文件夹权限 + NodeAuthProcessor.getInstance().refresh(); + templateDirTree.refresh(); + DesignerFrameFileDealerPane.getInstance().refreshRightToolBarBy(null); + FineLoggerFactory.getLogger().info(Toolkit.i18nText("Fine-Design_Basic_Template_File_Tree_Refresh_Successfully") + "!"); + } + + /** + * 刷新 + */ + public void refreshDockingView() { + templateDirTree.setFileNodeFilter(new IOFileNodeFilter(FRContext.getFileNodes().getSupportedTypes())); + templateDirTree.refreshEnv(); + } + +} diff --git a/designer-base/src/main/java/com/fr/design/file/TemplateTreePane.java b/designer-base/src/main/java/com/fr/design/file/TemplateTreePane.java index bc5e0da6b..1b298b9b1 100644 --- a/designer-base/src/main/java/com/fr/design/file/TemplateTreePane.java +++ b/designer-base/src/main/java/com/fr/design/file/TemplateTreePane.java @@ -7,7 +7,6 @@ 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.icontainer.UIScrollPane; import com.fr.design.gui.itree.filetree.TemplateFileTree; import com.fr.design.gui.itree.refreshabletree.ExpandMutableTreeNode; import com.fr.design.i18n.Toolkit; @@ -17,6 +16,7 @@ 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.searcher.control.pane.TemplateSearchRemindPane; import com.fr.file.FILE; import com.fr.file.FileNodeFILE; import com.fr.file.filetree.FileNode; @@ -74,19 +74,19 @@ public class TemplateTreePane extends JPanel implements FileOperations { } private TemplateFileTree reportletsTree; + private TemplateSearchRemindPane remindPane; private FileToolbarStateChangeListener toolBarStateChangeListener; private TemplateTreePane() { this.setLayout(FRGUIPaneFactory.createBorderLayout()); this.setPreferredSize(new Dimension(250, super.getPreferredSize().height)); - JPanel contentPane = FRGUIPaneFactory.createBorderLayout_S_Pane(); - this.add(contentPane, BorderLayout.CENTER); reportletsTree = new TemplateFileTree(); + + remindPane = new TemplateSearchRemindPane(getTemplateFileTree()); + this.add(remindPane, BorderLayout.CENTER); + ToolTipManager.sharedInstance().registerComponent(reportletsTree); - UIScrollPane scrollPane = new UIScrollPane(reportletsTree); - scrollPane.setBorder(null); - contentPane.add(scrollPane, BorderLayout.CENTER); FileLockStateObservable.getInstance().addObserver(new Observer() { @Override @@ -113,6 +113,8 @@ public class TemplateTreePane extends JPanel implements FileOperations { TemplateTreeDefineProcessor processor = ExtraDesignClassManager.getInstance().getSingle(TemplateTreeDefineProcessor.XML_TAG); if (processor != null) { processor.rightClickAction(e); + } else { + DefaultTemplateTreeDefineProcessor.getInstance().rightClickAction(e); } } } @@ -291,6 +293,27 @@ public class TemplateTreePane extends JPanel implements FileOperations { 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(); + } + if (reportletsTree.getSelectedFileNode().isDirectory()) { + reportletsTree.refreshDir(Objects.requireNonNull(reportletsTree.getSelectionPath())); + } else { + reportletsTree.refreshParent(Objects.requireNonNull(reportletsTree.getSelectionPath())); + } + DesignerFrameFileDealerPane.getInstance().refreshRightToolBarBy(null); + FineLoggerFactory.getLogger().info(Toolkit.i18nText("Fine-Design_Basic_Template_File_Tree_Refresh_Successfully") + "!"); + } + + /** * 删除文件 * 文件夹和文件均可删除 @@ -519,9 +542,13 @@ public class TemplateTreePane extends JPanel implements FileOperations { return false; } DefaultMutableTreeNode currentTreeNode = (DefaultMutableTreeNode) treePath.getLastPathComponent(); - TreeNode parentTreeNode = currentTreeNode.getParent(); - - Enumeration children = parentTreeNode.children(); + Enumeration children; + if (reportletsTree.getSelectedFileNode().isDirectory()) { + children = currentTreeNode.children(); + } else { + TreeNode parentTreeNode = currentTreeNode.getParent(); + children = parentTreeNode.children(); + } while (children.hasMoreElements()) { DefaultMutableTreeNode childNode = (DefaultMutableTreeNode) children.nextElement(); diff --git a/designer-base/src/main/java/com/fr/design/gui/itree/filetree/EnvFileTree.java b/designer-base/src/main/java/com/fr/design/gui/itree/filetree/EnvFileTree.java index a748fa320..d61b58cf1 100644 --- a/designer-base/src/main/java/com/fr/design/gui/itree/filetree/EnvFileTree.java +++ b/designer-base/src/main/java/com/fr/design/gui/itree/filetree/EnvFileTree.java @@ -52,40 +52,6 @@ public class EnvFileTree extends RefreshableJTree { /*一些自己的 init 放在这里,防止直接错误重写了父类的 init 方法导致子类不能使用 CheckBoxTree 的一些特性。*/ this.putClientProperty("JTree.lineStyle", "Angled"); - // CellRenderer - // 这里新建一个Label作为render是因为JTree在动态刷新的时候,节点上render画布的的宽度不会变,会使得一部分比较长的数据显示为 - DefaultTreeCellRenderer fileTreeCellRenderer = new DefaultTreeCellRenderer() { - - @Override - public Component getTreeCellRendererComponent(JTree tree, Object value, boolean selected, boolean expanded, boolean leaf, int row, - boolean hasFocus) { - super.getTreeCellRendererComponent(tree, value, selected, expanded, leaf, row, hasFocus); - ExpandMutableTreeNode treeNode = (ExpandMutableTreeNode) value; - Object userObj = treeNode.getUserObject(); - if (userObj instanceof FileNode) { - FileNode node = (FileNode) userObj; - String lock = node.getLock(); - String name = node.getName(); - if (treeNode.hasFullAuthority()) { - if (lock != null && !lock.equals(node.getUserID())) { - name = name + Toolkit.i18nText("Fine-Design_Basic_Template_Status_Locked", "(", ")"); - } - this.setIcon(FileTreeIcon.getIcon(node)); - } else { - this.setIcon(FileTreeIcon.getFolderHalfImageIcon()); - } - this.setText(name); - } else if (userObj == PENDING) { - this.setIcon(null); - this.setText(PENDING.toString()); - } - this.setBorder(BorderFactory.createEmptyBorder(1, 0, 1, 0)); - this.setBackgroundNonSelectionColor(UIConstants.TREE_BACKGROUND); - this.setTextSelectionColor(Color.WHITE); - this.setBackgroundSelectionColor(UIConstants.FLESH_BLUE); - return this; - } - }; this.setCellRenderer(fileTreeCellRenderer); this.setRootVisible(false); @@ -93,6 +59,45 @@ public class EnvFileTree extends RefreshableJTree { this.setEditable(false); } + // CellRenderer + // 这里新建一个Label作为render是因为JTree在动态刷新的时候,节点上render画布的的宽度不会变,会使得一部分比较长的数据显示为 + DefaultTreeCellRenderer fileTreeCellRenderer = new DefaultTreeCellRenderer() { + + @Override + public Component getTreeCellRendererComponent(JTree tree, Object value, boolean selected, boolean expanded, boolean leaf, int row, + boolean hasFocus) { + super.getTreeCellRendererComponent(tree, value, selected, expanded, leaf, row, hasFocus); + ExpandMutableTreeNode treeNode = (ExpandMutableTreeNode) value; + Object userObj = treeNode.getUserObject(); + if (userObj instanceof FileNode) { + FileNode node = (FileNode) userObj; + String lock = node.getLock(); + String name = node.getName(); + if (treeNode.hasFullAuthority()) { + if (lock != null && !lock.equals(node.getUserID())) { + name = name + Toolkit.i18nText("Fine-Design_Basic_Template_Status_Locked", "(", ")"); + } + this.setIcon(FileTreeIcon.getIcon(node)); + } else { + this.setIcon(FileTreeIcon.getFolderHalfImageIcon()); + } + this.setText(name); + } else if (userObj == PENDING) { + this.setIcon(null); + this.setText(PENDING.toString()); + } + this.setBorder(BorderFactory.createEmptyBorder(1, 0, 1, 0)); + this.setBackgroundNonSelectionColor(UIConstants.TREE_BACKGROUND); + this.setTextSelectionColor(Color.WHITE); + this.setBackgroundSelectionColor(UIConstants.FLESH_BLUE); + return this; + } + }; + + public DefaultTreeCellRenderer getFileTreeCellRenderer() { + return fileTreeCellRenderer; + } + private void setTreeRootPath(String path) { if (path == null) { path = ""; diff --git a/designer-base/src/main/java/com/fr/design/gui/itree/filetree/TemplateDirTree.java b/designer-base/src/main/java/com/fr/design/gui/itree/filetree/TemplateDirTree.java new file mode 100644 index 000000000..10388fa32 --- /dev/null +++ b/designer-base/src/main/java/com/fr/design/gui/itree/filetree/TemplateDirTree.java @@ -0,0 +1,54 @@ +package com.fr.design.gui.itree.filetree; + +import com.fr.base.FRContext; +import com.fr.design.gui.itree.refreshabletree.ExpandMutableTreeNode; +import com.fr.design.mainframe.manager.search.TemplateDirTreeSearchManager; +import com.fr.design.mainframe.manager.search.TemplateTreeSearchManager; +import com.fr.file.filetree.FileNode; + +import java.util.Arrays; +import java.util.Map; +import java.util.stream.Collectors; + +/* + * 目录树 + */ +public class TemplateDirTree extends TemplateFileTree { + + public TemplateDirTree() { + super(); + } + + + /** + * 查找所有目录子节点 + * + * @param path + * @return + */ + public FileNode[] listFile(String path) { + return Arrays.stream(FRContext.getFileNodes().list(path)).filter(FileNode::isDirectory).toArray(FileNode[]::new); + } + + + /** + * 搜索时刷新目录树 + * @param root + */ + public void refreshTreeNode4TreeSearch(ExpandMutableTreeNode root) { + if (interceptRefresh(root)) { + return; + } + currentTreeMode.clear(); + allTreeNode = TemplateTreeSearchManager.getInstance().allFileNodes().entrySet().stream().collect( + Collectors.toMap(Map.Entry::getKey, entry -> fileNodeArray2TreeNodeArray(new FileNode[]{entry.getValue()})[0])); + root.removeAllChildren(); + + FileNode[] treeNodes = TemplateDirTreeSearchManager.getInstance().matchesNode(); + for (FileNode fileNode : treeNodes) { + ExpandMutableTreeNode treeNode = fileNodeArray2TreeNodeArray(new FileNode[]{fileNode})[0]; + addToTreeModel(root, treeNode); + } + } + +} diff --git a/designer-base/src/main/java/com/fr/design/gui/itree/filetree/TemplateFileTree.java b/designer-base/src/main/java/com/fr/design/gui/itree/filetree/TemplateFileTree.java index b1f641b3b..e50a3145b 100644 --- a/designer-base/src/main/java/com/fr/design/gui/itree/filetree/TemplateFileTree.java +++ b/designer-base/src/main/java/com/fr/design/gui/itree/filetree/TemplateFileTree.java @@ -6,10 +6,12 @@ import com.fr.design.ExtraDesignClassManager; import com.fr.design.file.NodeAuthProcessor; import com.fr.design.gui.itree.refreshabletree.ExpandMutableTreeNode; import com.fr.design.mainframe.App; +import com.fr.design.mainframe.manager.search.TemplateTreeSearchManager; import com.fr.file.filetree.FileNode; import com.fr.log.FineLoggerFactory; import com.fr.stable.ArrayUtils; import com.fr.stable.StableUtils; +import com.fr.stable.collections.CollectionUtils; import com.fr.stable.project.ProjectConstants; import javax.swing.text.Position; @@ -17,15 +19,22 @@ import javax.swing.tree.DefaultTreeModel; import javax.swing.tree.TreePath; import java.util.ArrayList; import java.util.Arrays; +import java.util.Comparator; import java.util.HashSet; import java.util.List; +import java.util.Map; import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.stream.Collectors; /* * 显示Env下的reportlets目录下面的所有cpt文件 */ public class TemplateFileTree extends EnvFileTree { + protected Map allTreeNode = new ConcurrentHashMap<>(); + protected Map> currentTreeMode = new ConcurrentHashMap<>(); + public TemplateFileTree() { super(ProjectConstants.REPORTLETS_NAME, null, null); } @@ -213,7 +222,7 @@ public class TemplateFileTree extends EnvFileTree { /* * 把FileNode[]转成ExpandMutableTreeNode[] */ - private ExpandMutableTreeNode[] fileNodeArray2TreeNodeArray(FileNode[] fileNodes) { + protected ExpandMutableTreeNode[] fileNodeArray2TreeNodeArray(FileNode[] fileNodes) { return NodeAuthProcessor.getInstance().parser2TreeNodeArray(fileNodes); } @@ -251,4 +260,106 @@ public class TemplateFileTree extends EnvFileTree { return new FileNode[0]; } + + @Override + public void refresh4TreeSearch() { + ExpandMutableTreeNode root = (ExpandMutableTreeNode) this.getModel().getRoot(); + NodeAuthProcessor.getInstance().fixTreeNodeAuth(root); + refreshTreeNode4TreeSearch(root); + sortTreeNode(root); + ((DefaultTreeModel) this.getModel()).reload(root); + root.expandCurrentTreeNode(this); + } + + /** + * 重新对整个目录树排序 + * + * @param root 根节点 + */ + private void sortTreeNode(ExpandMutableTreeNode root) { + List childFileNode = new ArrayList<>(); + for (int i = 0; i < root.getChildCount(); i++) { + ExpandMutableTreeNode treeNode = (ExpandMutableTreeNode) root.getChildAt(i); + childFileNode.add(treeNode); + } + childFileNode = childFileNode.stream().sorted(Comparator.comparing(treeNode -> ((FileNode) (treeNode.getUserObject())), + new FileNodeComparator(FileNodeConstants.getSupportFileTypes()))).collect(Collectors.toList()); + root.removeAllChildren(); + root.addChildTreeNodes(childFileNode.toArray(new ExpandMutableTreeNode[0])); + for (ExpandMutableTreeNode treeNode : childFileNode) { + if (!treeNode.isLeaf()) { + sortTreeNode(treeNode); + } + } + } + + protected void refreshTreeNode4TreeSearch(ExpandMutableTreeNode root) { + if (interceptRefresh(root)) { + return; + } + + if (!TemplateTreeSearchManager.getInstance().isRefreshing()) { + currentTreeMode.clear(); + allTreeNode = TemplateTreeSearchManager.getInstance().allFileNodes().entrySet().stream().collect( + Collectors.toMap(Map.Entry::getKey, entry -> fileNodeArray2TreeNodeArray(new FileNode[]{entry.getValue()})[0])); + TemplateTreeSearchManager.getInstance().setRefreshing(true); + root.removeAllChildren(); + } + + FileNode[] treeNodes = TemplateTreeSearchManager.getInstance().matchesNode(); + for (FileNode fileNode : treeNodes) { + ExpandMutableTreeNode treeNode = fileNodeArray2TreeNodeArray(new FileNode[]{fileNode})[0]; + addToTreeModel(root, treeNode); + } + } + + protected void addToTreeModel(ExpandMutableTreeNode root, ExpandMutableTreeNode treeNode) { + + FileNode fileNode = (FileNode) treeNode.getUserObject(); + String parentPath = fileNode.getParent(); + removePending(treeNode); + + while (!ProjectConstants.REPORTLETS_NAME.equals(parentPath)) { + ExpandMutableTreeNode parentNode = allTreeNode.get(parentPath); + if (parentNode == null) { + FineLoggerFactory.getLogger().info("{} parent path {} is not found in allTreeNode.", fileNode.getEnvPath(), parentPath); + break; + } + parentNode.setExpanded(true); + removePending(parentNode); + if (notContained(parentPath, treeNode)) { + parentNode.add(treeNode); + } + parentPath = ((FileNode) (parentNode.getUserObject())).getParent(); + treeNode = parentNode; + } + + //最外层节点 + if (notContained(ProjectConstants.REPORTLETS_NAME, treeNode)) { + root.add(treeNode); + } + } + + private boolean notContained(String parentPath, ExpandMutableTreeNode treeNode) { + List childList = currentTreeMode.getOrDefault(parentPath, new ArrayList<>()); + String name = ((FileNode) (treeNode.getUserObject())).getName(); + boolean result = false; + if (CollectionUtils.isEmpty(childList)) { + childList.add(name); + result = true; + } else { + if (!childList.contains(name)) { + childList.add(name); + result = true; + } + } + currentTreeMode.put(parentPath, childList); + return result; + } + + protected void removePending(ExpandMutableTreeNode treeNode) { + if (treeNode.getChildCount() >= 1 && ((ExpandMutableTreeNode) treeNode.getFirstChild()).getUserObject() == PENDING) { + treeNode.remove(0); + } + } } \ No newline at end of file diff --git a/designer-base/src/main/java/com/fr/design/gui/itree/refreshabletree/ExpandMutableTreeNode.java b/designer-base/src/main/java/com/fr/design/gui/itree/refreshabletree/ExpandMutableTreeNode.java index 48a4d933b..af31420a2 100644 --- a/designer-base/src/main/java/com/fr/design/gui/itree/refreshabletree/ExpandMutableTreeNode.java +++ b/designer-base/src/main/java/com/fr/design/gui/itree/refreshabletree/ExpandMutableTreeNode.java @@ -92,4 +92,12 @@ public class ExpandMutableTreeNode extends DefaultMutableTreeNode { this.add(newChildNodes[i]); } } + + @Override + public Object clone() { + ExpandMutableTreeNode cloned = (ExpandMutableTreeNode) super.clone(); + cloned.setExpanded(this.isExpanded); + cloned.setFullAuthority(this.hasFullAuthority); + return cloned; + } } \ No newline at end of file diff --git a/designer-base/src/main/java/com/fr/design/gui/itree/refreshabletree/RefreshableJTree.java b/designer-base/src/main/java/com/fr/design/gui/itree/refreshabletree/RefreshableJTree.java index 161c1c7c7..338038c36 100644 --- a/designer-base/src/main/java/com/fr/design/gui/itree/refreshabletree/RefreshableJTree.java +++ b/designer-base/src/main/java/com/fr/design/gui/itree/refreshabletree/RefreshableJTree.java @@ -150,6 +150,11 @@ public abstract class RefreshableJTree extends CheckBoxTree { refresh((ExpandMutableTreeNode) path.getParentPath().getLastPathComponent(), StringUtils.EMPTY); } + //刷新指定目录 + public void refreshDir(TreePath path) { + refresh((ExpandMutableTreeNode) path.getLastPathComponent(), StringUtils.EMPTY); + } + public void refreshChildByName(String childName) { refresh((ExpandMutableTreeNode) this.getModel().getRoot(), childName); } diff --git a/designer-base/src/main/java/com/fr/design/mainframe/DesignerFrameFileDealerPane.java b/designer-base/src/main/java/com/fr/design/mainframe/DesignerFrameFileDealerPane.java index ab30582dd..0e2649fb8 100644 --- a/designer-base/src/main/java/com/fr/design/mainframe/DesignerFrameFileDealerPane.java +++ b/designer-base/src/main/java/com/fr/design/mainframe/DesignerFrameFileDealerPane.java @@ -3,11 +3,13 @@ package com.fr.design.mainframe; import com.fr.base.BaseUtils; import com.fr.base.extension.FileExtension; import com.fr.base.vcs.DesignerMode; -import com.fr.chartx.TwoTuple; import com.fr.design.DesignModelAdapter; import com.fr.design.DesignerEnvManager; import com.fr.design.ExtraDesignClassManager; import com.fr.design.actions.UpdateAction; +import com.fr.design.actions.file.DelFileAction; +import com.fr.design.actions.file.LocateAction; +import com.fr.design.actions.file.RenameAction; import com.fr.design.constants.UIConstants; import com.fr.design.data.DesignTableDataManager; import com.fr.design.data.datapane.TableDataTreePane; @@ -29,6 +31,8 @@ import com.fr.design.i18n.Toolkit; import com.fr.design.layout.FRGUIPaneFactory; import com.fr.design.layout.TableLayout; import com.fr.design.layout.TableLayoutHelper; +import com.fr.design.mainframe.manager.search.TemplateTreeSearchManager; +import com.fr.design.mainframe.manager.search.searcher.control.pane.TemplateTreeSearchToolbarPane; import com.fr.design.mainframe.vcs.common.VcsHelper; import com.fr.design.mainframe.vcs.ui.FileVersionsPanel; import com.fr.design.menu.KeySetUtils; @@ -38,9 +42,6 @@ import com.fr.design.roleAuthority.RolesAlreadyEditedPane; import com.fr.design.ui.util.UIUtil; import com.fr.design.utils.DesignUtils; import com.fr.design.utils.gui.GUICoreUtils; -import com.fr.event.Event; -import com.fr.event.EventDispatcher; -import com.fr.file.FileNodeFILE; import com.fr.file.filetree.FileNode; import com.fr.general.ComparatorUtils; import com.fr.general.GeneralContext; @@ -81,14 +82,15 @@ import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; +import java.util.regex.Pattern; import static javax.swing.JOptionPane.WARNING_MESSAGE; public class DesignerFrameFileDealerPane extends JPanel implements FileToolbarStateChangeListener, ResponseDataSourceChange { - public static final Event> TEMPLATE_RENAME = new Event>() { - }; + private static final String FILE = "file"; + public static final String FILE_NAME_LIMIT = "^[^/\\\\:<>\\*\\|\"\\?]+$"; private static volatile DesignerFrameFileDealerPane THIS; static { @@ -144,6 +146,13 @@ public class DesignerFrameFileDealerPane extends JPanel implements FileToolbarSt private VcsAction vcsAction = new VcsAction(); + //搜索 + private SwitchAction switchAction = new SwitchAction(); + private TemplateTreeSearchToolbarPane searchToolbarPane; + + //定位、收起 + private LocateAction locateAction = new LocateAction(); + private CollapseAllAction collapseAllAction = new CollapseAllAction(); private DesignerFrameFileDealerPane() { @@ -158,7 +167,10 @@ public class DesignerFrameFileDealerPane extends JPanel implements FileToolbarSt tooBarPane.add(parent, BorderLayout.CENTER); tooBarPane.add(new UIMenuHighLight(), BorderLayout.SOUTH); - add(tooBarPane, BorderLayout.NORTH); + searchToolbarPane = new TemplateTreeSearchToolbarPane(toolBar); + searchToolbarPane.setPreferredSize(new Dimension(this.getWidth(), 23)); + + add(searchToolbarPane, BorderLayout.NORTH); CardLayout card; JPanel cardPane = new JPanel(card = new CardLayout()); cardPane.add(TemplateTreePane.getInstance(), FILE); @@ -285,6 +297,7 @@ public class DesignerFrameFileDealerPane extends JPanel implements FileToolbarSt toolbarDef.addShortCut(shortCut); } addVcsAction(toolbarDef); + toolbarDef.addShortCut(locateAction, collapseAllAction, switchAction); toolbarDef.updateToolBar(toolBar); resetActionStatus(); refresh(); @@ -422,6 +435,36 @@ public class DesignerFrameFileDealerPane extends JPanel implements FileToolbarSt } + private class SwitchAction extends UpdateAction { + + public SwitchAction() { + this.setName(Toolkit.i18nText("Fine-Design_Basic_Search")); + this.setMnemonic('S'); + this.setSmallIcon("/com/fr/design/images/data/search"); + } + + @Override + public void actionPerformed(ActionEvent e) { + // 交换层级 + searchToolbarPane.switchPane(TemplateTreeSearchToolbarPane.SEARCH_PANE); + TemplateTreePane.getInstance().refreshDockingView(); + TemplateTreeSearchManager.getInstance().switchToSearch(TemplateTreePane.getInstance().getTemplateFileTree()); + } + } + + public class CollapseAllAction extends UpdateAction { + + public CollapseAllAction() { + this.setName(Toolkit.i18nText("Fine-Design_Basic_Collapse_All")); + this.setSmallIcon("/com/fr/design/images/FileDealerPaneIcon/collapse-all.png"); + } + + @Override + public void actionPerformed(ActionEvent e) { + TemplateTreePane.getInstance().refreshDockingView(); + } + } + /** * 版本管理 */ @@ -537,72 +580,6 @@ public class DesignerFrameFileDealerPane extends JPanel implements FileToolbarSt } } - /* - * 重命名文件 - */ - private class RenameAction extends UpdateAction { - - public RenameAction() { - - this.setName(Toolkit.i18nText("Fine-Design_Basic_Rename")); - this.setSmallIcon("/com/fr/design/images/FileDealerPaneIcon/rename"); - } - - @Override - public void actionPerformed(ActionEvent evt) { - if (!selectedOperation.access()) { - FineJOptionPane.showMessageDialog(DesignerContext.getDesignerFrame(), - Toolkit.i18nText("Fine-Design_Basic_Template_Permission_Denied"), - Toolkit.i18nText("Fine-Design_Basic_Alert"), - WARNING_MESSAGE); - return; - } - - FileNode node = selectedOperation.getFileNode(); - String lock = node.getLock(); - if (lock != null && !lock.equals(node.getUserID())) { - // 提醒被锁定模板无法重命名 - FineJOptionPane.showMessageDialog(DesignerContext.getDesignerFrame(), - Toolkit.i18nText("Fine-Design_Basic_Unable_Rename_Locked_File"), - Toolkit.i18nText("Fine-Design_Basic_Alert"), - WARNING_MESSAGE); - return; - } - - new FileRenameDialog(node); - MutilTempalteTabPane.getInstance().repaint(); - stateChange(); - } - - } - - /* - * 删除指定文件 - */ - private class DelFileAction extends UpdateAction { - - public DelFileAction() { - - this.setName(Toolkit.i18nText("Fine-Design_Basic_Remove")); - this.setSmallIcon("/com/fr/design/images/FileDealerPaneIcon/remove"); - } - - @Override - public void actionPerformed(ActionEvent evt) { - - if (!selectedOperation.access()) { - FineJOptionPane.showMessageDialog(DesignerContext.getDesignerFrame(), - Toolkit.i18nText("Fine-Design_Basic_Template_Permission_Denied"), - Toolkit.i18nText("Fine-Design_Basic_Alert"), - WARNING_MESSAGE); - return; - } - selectedOperation.deleteFile(); - stateChange(); - DesignerContext.getDesignerFrame().setTitle(); - } - } - public void refreshRightToolBarBy(FileNode fileNode) { if (rightToolBar != null) { boolean locked = fileNode != null @@ -616,225 +593,6 @@ public class DesignerFrameFileDealerPane extends JPanel implements FileToolbarSt } } - /** - * 重命名对话框 - * 支持快捷键Enter,ESC - */ - private class FileRenameDialog extends JDialog { - - private UITextField nameField; - - private UILabel warnLabel; - - private UIButton confirmButton; - - /** - * 操作的节点 - */ - private FileNodeFILE fnf; - - - private FileRenameDialog(FileNode node) { - if (node == null) { - return; - } - fnf = new FileNodeFILE(node); - - String oldName = fnf.getName(); - String suffix = fnf.isDirectory() ? StringUtils.EMPTY : oldName.substring(oldName.lastIndexOf(CoreConstants.DOT), oldName.length()); - oldName = StringUtils.replaceLast(oldName, suffix, StringUtils.EMPTY); - this.setLayout(new BorderLayout()); - this.setModal(true); - - // 输入框前提示 - UILabel newNameLabel = new UILabel(Toolkit.i18nText( - fnf.isDirectory() ? - "Fine-Design_Basic_Enter_New_Folder_Name" : "Fine-Design_Basic_Enter_New_File_Name") - ); - newNameLabel.setHorizontalAlignment(SwingConstants.RIGHT); - newNameLabel.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 10)); - //newNameLabel.setPreferredSize(new Dimension(118, 15)); - - // 重命名输入框 - nameField = new UITextField(oldName); - nameField.getDocument().addDocumentListener(new DocumentListener() { - - @Override - public void changedUpdate(DocumentEvent e) { - validInput(); - } - - @Override - public void insertUpdate(DocumentEvent e) { - validInput(); - } - - @Override - public void removeUpdate(DocumentEvent e) { - validInput(); - } - }); - nameField.selectAll(); - nameField.setPreferredSize(new Dimension(170, 20)); - - JPanel topPanel = new JPanel(new FlowLayout(FlowLayout.LEFT, 0, 5)); - topPanel.setBorder(BorderFactory.createEmptyBorder(15, 15, 0, 15)); - topPanel.add(newNameLabel); - topPanel.add(nameField); - - // 增加enter以及esc快捷键的支持 - nameField.addKeyListener(new KeyAdapter() { - @Override - public void keyPressed(KeyEvent e) { - if (e.getKeyCode() == KeyEvent.VK_ESCAPE) { - dispose(); - } else if (e.getKeyCode() == KeyEvent.VK_ENTER) { - if (confirmButton.isEnabled()) { - confirmClose(); - } - } - } - }); - // 重名提示 - warnLabel = new UILabel(); - warnLabel.setPreferredSize(new Dimension(300, 50)); - warnLabel.setHorizontalAlignment(SwingConstants.LEFT); - warnLabel.setVerticalAlignment(SwingConstants.TOP); - warnLabel.setForeground(Color.RED); - warnLabel.setVisible(false); - - JPanel midPanel = new JPanel(new BorderLayout()); - midPanel.setBorder(BorderFactory.createEmptyBorder(0, 15, 0, 15)); - midPanel.add(warnLabel, BorderLayout.WEST); - - // 确认按钮 - confirmButton = new UIButton(Toolkit.i18nText("Fine-Design_Basic_Confirm")); - confirmButton.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - confirmClose(); - } - }); - - // 取消按钮 - UIButton cancelButton = new UIButton(Toolkit.i18nText("Fine-Design_Basic_Cancel")); - - cancelButton.addActionListener(new ActionListener() { - - @Override - public void actionPerformed(ActionEvent e) { - dispose(); - } - }); - - - JPanel buttonsPane = new JPanel(new FlowLayout(FlowLayout.RIGHT, 10, 0)); - buttonsPane.setBorder(BorderFactory.createEmptyBorder(10, 15, 10, 10)); - buttonsPane.add(confirmButton); - buttonsPane.add(cancelButton); - - this.add( - TableLayoutHelper.createTableLayoutPane( - new Component[][]{ - new Component[]{topPanel}, - new Component[]{midPanel}, - new Component[]{buttonsPane} - }, - new double[]{TableLayout.FILL, TableLayout.PREFERRED, TableLayout.PREFERRED}, - new double[]{TableLayout.FILL} - ), - BorderLayout.CENTER); - - - this.setSize(340, 200); - this.setTitle(Toolkit.i18nText("Fine-Design_Basic_Rename")); - this.setResizable(false); - this.setAlwaysOnTop(true); - this.setIconImage(BaseUtils.readImage("/com/fr/base/images/oem/logo.png")); - this.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE); - GUICoreUtils.centerWindow(this); - this.setVisible(true); - } - - private void confirmClose() { - - String userInput = nameField.getText().trim(); - // 处理不合法的文件夹名称 - userInput = userInput.replaceAll("[\\\\/:*?\"<>|]", StringUtils.EMPTY); - - String path = FilenameUtils.standard(fnf.getPath()); - - String oldName = fnf.getName(); - String suffix = fnf.isDirectory() ? StringUtils.EMPTY : oldName.substring(oldName.lastIndexOf(CoreConstants.DOT), oldName.length()); - oldName = StringUtils.replaceLast(oldName, suffix, StringUtils.EMPTY); - - // 输入为空或者没有修改 - if (ComparatorUtils.equals(userInput, oldName)) { - this.dispose(); - return; - } - - String parentPath = FilenameUtils.standard(fnf.getParent().getPath()); - - // 简单执行old new 替换是不可行的,例如 /abc/abc/abc/abc/ - String newPath = parentPath + CoreConstants.SEPARATOR + userInput + suffix; - this.dispose(); - - //模版重命名 - boolean success = selectedOperation.rename(fnf, path, newPath); - - if (success) { - EventDispatcher.fire(TEMPLATE_RENAME, new TwoTuple<>(path, newPath)); - HistoryTemplateListCache.getInstance().rename(fnf, path, newPath); - DesignerEnvManager.getEnvManager().replaceRecentOpenedFilePath(fnf.isDirectory(), path, newPath); - selectedOperation.refresh(); - DesignerContext.getDesignerFrame().setTitle(); - } else { - FineJOptionPane.showConfirmDialog(DesignerContext.getDesignerFrame(), - Toolkit.i18nText("Fine-Design_Basic_Rename_Failure"), - Toolkit.i18nText("Fine-Design_Basic_Error"), - JOptionPane.DEFAULT_OPTION, - JOptionPane.ERROR_MESSAGE); - } - } - - - private void validInput() { - - String userInput = nameField.getText().trim(); - - String oldName = fnf.getName(); - String suffix = fnf.isDirectory() ? StringUtils.EMPTY : oldName.substring(oldName.lastIndexOf(CoreConstants.DOT), oldName.length()); - oldName = oldName.replaceAll(suffix, StringUtils.EMPTY); - - if (StringUtils.isEmpty(userInput)) { - confirmButton.setEnabled(false); - return; - } - - if (ComparatorUtils.equals(userInput, oldName)) { - warnLabel.setVisible(false); - confirmButton.setEnabled(true); - return; - } - - if (selectedOperation.duplicated(userInput, suffix)) { - nameField.selectAll(); - // 如果文件名已存在,则灰掉确认按钮 - warnLabel.setText( - Toolkit.i18nText(fnf.isDirectory() ? - "Fine-Design_Basic_Folder_Name_Duplicate" : - "Fine-Design_Basic_Template_File_Name_Duplicate", - userInput)); - warnLabel.setVisible(true); - confirmButton.setEnabled(false); - } else { - warnLabel.setVisible(false); - confirmButton.setEnabled(true); - } - } - } - /** * 新建文件夹对话框 @@ -967,16 +725,20 @@ public class DesignerFrameFileDealerPane extends JPanel implements FileToolbarSt private void confirmClose() { String userInput = nameField.getText().trim(); - // 处理不合法的文件夹名称 - userInput = userInput.replaceAll("[\\\\/:*?\"<>|]", StringUtils.EMPTY); if (StringUtils.isEmpty(userInput)) { return; } //新建文件夹 + FileNode selectedFileNode = selectedOperation.getFileNode(); + String parentPath = selectedFileNode.getParent(); + if (selectedFileNode.isDirectory()) { + //目录的话,父目录就是所选目录 + parentPath = FilenameUtils.standard(parentPath + CoreConstants.SEPARATOR + selectedFileNode.getName()); + } boolean success = selectedOperation.mkdir( - FilenameUtils.standard(selectedOperation.getFileNode().getParent() + CoreConstants.SEPARATOR + userInput) + FilenameUtils.standard(parentPath + CoreConstants.SEPARATOR + userInput) ); selectedOperation.refresh(); this.dispose(); @@ -999,13 +761,11 @@ public class DesignerFrameFileDealerPane extends JPanel implements FileToolbarSt return; } - if (selectedOperation.duplicated(userInput, StringUtils.EMPTY)) { + String errorMsg = doCheck(userInput, StringUtils.EMPTY); + if (StringUtils.isNotEmpty(errorMsg)) { nameField.selectAll(); - // 如果文件名已存在,则灰掉确认按钮 - warnLabel.setText( - Toolkit.i18nText( - "Fine-Design_Basic_Folder_Name_Duplicate", - userInput)); + // 如果检测出错,则灰掉确认按钮并提示 + warnLabel.setText(errorMsg); warnLabel.setVisible(true); confirmButton.setEnabled(false); } else { @@ -1015,6 +775,17 @@ public class DesignerFrameFileDealerPane extends JPanel implements FileToolbarSt } } + private String doCheck (String userInput, String suffix) { + String errorMsg = StringUtils.EMPTY; + if (selectedOperation.duplicated(userInput, suffix)) { + errorMsg = Toolkit.i18nText("Fine-Design_Basic_Folder_Name_Duplicate", userInput); + } + if (!Pattern.compile(FILE_NAME_LIMIT).matcher(userInput).matches()) { + errorMsg = Toolkit.i18nText("Fine-Design_Basic_Template_Name_Illegal"); + } + return errorMsg; + } + private boolean isEnable() { List> templates = HistoryTemplateListCache.getInstance().getHistoryList(); for (JTemplate template : templates) { diff --git a/designer-base/src/main/java/com/fr/design/mainframe/manager/clip/TemplateTreeClipboard.java b/designer-base/src/main/java/com/fr/design/mainframe/manager/clip/TemplateTreeClipboard.java new file mode 100644 index 000000000..f950701f5 --- /dev/null +++ b/designer-base/src/main/java/com/fr/design/mainframe/manager/clip/TemplateTreeClipboard.java @@ -0,0 +1,63 @@ +package com.fr.design.mainframe.manager.clip; + +import com.fr.design.gui.itree.refreshabletree.ExpandMutableTreeNode; + +import java.util.ArrayList; +import java.util.List; + +public class TemplateTreeClipboard { + + private List clip = new ArrayList<>(); + + private static class Holder { + private static final TemplateTreeClipboard INSTANCE = new TemplateTreeClipboard(); + } + + private TemplateTreeClipboard() { + } + + public static TemplateTreeClipboard getInstance() { + return TemplateTreeClipboard.Holder.INSTANCE; + } + + public List transferNameObjectArray2Map(ExpandMutableTreeNode[] selectedTreeNodes) { + List resultMap = new ArrayList<>(); + if (selectedTreeNodes == null) { + return resultMap; + } + for (ExpandMutableTreeNode selectTreeNode : selectedTreeNodes) { + ExpandMutableTreeNode cloned = (ExpandMutableTreeNode) selectTreeNode.clone(); + if (cloned != null) { + resultMap.add(cloned); + } + } + return resultMap; + } + + /** + * 添加选中的模板数据到剪切板,覆盖原本剪切板内数据 + * + * @param copyList + * @return + */ + public void addToClip(List copyList) { + this.clip = copyList; + } + + /** + * 取出剪切板内的所有模板数据,剪切板不清空 + * + * @return + */ + public List takeFromClip() { + return clip; + } + + /** + * 清空剪切板 + */ + public void reset() { + clip.clear(); + } + +} diff --git a/designer-base/src/main/java/com/fr/design/mainframe/manager/search/TemplateDirTreeSearchManager.java b/designer-base/src/main/java/com/fr/design/mainframe/manager/search/TemplateDirTreeSearchManager.java new file mode 100644 index 000000000..dcf8a5cb1 --- /dev/null +++ b/designer-base/src/main/java/com/fr/design/mainframe/manager/search/TemplateDirTreeSearchManager.java @@ -0,0 +1,190 @@ +package com.fr.design.mainframe.manager.search; + +import com.fr.design.file.TemplateDirTreePane; +import com.fr.design.gui.itree.filetree.TemplateDirTree; +import com.fr.design.mainframe.manager.search.searcher.TemplateDirTreeSearcher; +import com.fr.design.mainframe.manager.search.searcher.TemplateTreeSearcher; +import com.fr.design.search.TreeSearchStatus; +import com.fr.design.search.event.TreeSearchStatusChangeEvent; +import com.fr.design.search.event.TreeSearchStatusChangeListener; +import com.fr.design.search.view.TreeSearchRendererHelper; +import com.fr.file.filetree.FileNode; +import com.fr.log.FineLoggerFactory; +import com.fr.stable.StringUtils; + +import javax.swing.SwingUtilities; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +public class TemplateDirTreeSearchManager { + + /** + * 文件树搜索器 + */ + private TemplateTreeSearcher treeSearcher; + + /** + * 搜索任务的状态 + */ + private TreeSearchStatus treeSearchStatus; + + /** + * 缓存上次搜索文本,避免重复搜索 + */ + private String lastSearchText; + + /** + * 存储与复原 原本模板的UI + */ + private TreeSearchRendererHelper rendererHelper; + + /** + * 搜索状态变化监听 + */ + private List listeners = new ArrayList<>(); + + private TemplateDirTreeSearchManager() { + init(); + } + + private void init() { + this.treeSearchStatus = TreeSearchStatus.NOT_IN_SEARCH_MODE; + } + + private static class Holder { + private static final TemplateDirTreeSearchManager INSTANCE = new TemplateDirTreeSearchManager(); + } + + public static TemplateDirTreeSearchManager getInstance() { + return TemplateDirTreeSearchManager.Holder.INSTANCE; + } + + public TreeSearchStatus getTreeSearchStatus() { + return treeSearchStatus; + } + + public void setTreeSearchStatus(TreeSearchStatus treeSearchStatus) { + this.treeSearchStatus = treeSearchStatus; + // 每次设置搜索状态,都触发下监听,让页面跟随变化 + SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + for (TreeSearchStatusChangeListener listener : listeners) { + listener.updateTreeSearchChange(new TreeSearchStatusChangeEvent(treeSearchStatus)); + } + } + }); + } + + public void registerTreeSearchStatusChangeListener(TreeSearchStatusChangeListener listener) { + listeners.add(listener); + } + + /** + * 获取当前的目录树 + * + * @return + */ + private TemplateDirTree getCurrentTemplateDirTree() { + return TemplateDirTreePane.getInstance().getTemplateDirTree(); + } + + public void beforeSearch(TemplateDirTree templateDirTree) { + setTreeSearchStatus(TreeSearchStatus.SEARCH_NOT_BEGIN); + rendererHelper = new TreeSearchRendererHelper(); + rendererHelper.save(templateDirTree.getFileTreeCellRenderer()); + treeSearcher = new TemplateDirTreeSearcher(); + FineLoggerFactory.getLogger().debug("switch to template dir search"); + treeSearcher.beforeSearch(templateDirTree); + } + + /** + * 开始搜索 + * + * @param searchText + */ + public void startSearch(String searchText) { + if (isRepeatSearch(searchText) || StringUtils.isEmpty(searchText)) { + return; + } + setTreeSearchStatus(TreeSearchStatus.SEARCHING); + rendererHelper.replaceTreeRenderer(getCurrentTemplateDirTree(), searchText); + FineLoggerFactory.getLogger().debug("start template dir search for search text: {}", searchText); + treeSearcher.startSearch(searchText); + } + + /** + * 中断搜索 + */ + public void stopSearch() { + setTreeSearchStatus(TreeSearchStatus.SEARCH_STOPPED); + FineLoggerFactory.getLogger().debug("stop template dir search for search text: {}", lastSearchText); + treeSearcher.stopSearch(); + } + + /** + * 搜索完成 + */ + public void completeSearch() { + setTreeSearchStatus(TreeSearchStatus.SEARCH_COMPLETED); + FineLoggerFactory.getLogger().debug("complete template dir search for search text: {}", lastSearchText); + treeSearcher.completeSearch(); + } + + /** + * 退出搜索模式 + */ + public void outOfSearchMode() { + setTreeSearchStatus(TreeSearchStatus.NOT_IN_SEARCH_MODE); + FineLoggerFactory.getLogger().info("out of template search"); + lastSearchText = null; + if (treeSearcher != null) { + treeSearcher.afterSearch(); + } + if (rendererHelper != null) { + rendererHelper.restore(getCurrentTemplateDirTree()); + } + } + + /** + * 所有匹配的目录节点 + * @return + */ + public FileNode[] matchesNode() { + return treeSearcher.getMatchSets().toArray(new FileNode[0]); + } + + public Map allFileNodes() { + return treeSearcher.getAllTemplates(); + } + + public boolean isMatchSetsEmpty() { + return treeSearcher.isMatchSetsEmpty(); + } + + public void restoreTreePane() { + setTreeSearchStatus(TreeSearchStatus.SEARCH_NOT_BEGIN); + lastSearchText = null; + if (rendererHelper != null) { + rendererHelper.restore(getCurrentTemplateDirTree()); + } + } + + /** + * 刷新树,更新搜索的结果 + */ + public void updateTemplateTree() { + getCurrentTemplateDirTree().refresh4TreeSearch(); + } + + private boolean isRepeatSearch(String searchText) { + boolean repeat = StringUtils.equals(lastSearchText, searchText); + lastSearchText = searchText; + return repeat; + } + + public boolean isInSearchMode() { + return getTreeSearchStatus() != TreeSearchStatus.NOT_IN_SEARCH_MODE; + } +} diff --git a/designer-base/src/main/java/com/fr/design/mainframe/manager/search/TemplateTreeSearchManager.java b/designer-base/src/main/java/com/fr/design/mainframe/manager/search/TemplateTreeSearchManager.java new file mode 100644 index 000000000..9ae3b8e23 --- /dev/null +++ b/designer-base/src/main/java/com/fr/design/mainframe/manager/search/TemplateTreeSearchManager.java @@ -0,0 +1,222 @@ +package com.fr.design.mainframe.manager.search; + +import com.fr.design.search.event.TreeSearchStatusChangeEvent; +import com.fr.design.search.event.TreeSearchStatusChangeListener; +import com.fr.design.search.TreeSearchStatus; +import com.fr.design.search.view.TreeSearchRendererHelper; +import com.fr.design.file.TemplateTreePane; +import com.fr.design.gui.itree.filetree.TemplateFileTree; +import com.fr.design.mainframe.manager.search.searcher.TemplateTreeSearcher; +import com.fr.file.filetree.FileNode; +import com.fr.log.FineLoggerFactory; +import com.fr.stable.StringUtils; + +import javax.swing.SwingUtilities; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.concurrent.atomic.AtomicBoolean; + +/** + * 文件树搜索管理器 + */ +public class TemplateTreeSearchManager { + + /** + * 文件树搜索器 + */ + private TemplateTreeSearcher treeSearcher; + + /** + * 搜索任务的状态 + */ + private TreeSearchStatus treeSearchStatus; + + /** + * 缓存上次搜索文本,避免重复搜索 + */ + private String lastSearchText; + + /** + * 存储与复原 原本模板的UI + */ + private TreeSearchRendererHelper rendererHelper; + + /** + * 是否在更新搜索结果的标识 + */ + private AtomicBoolean isRefreshing = new AtomicBoolean(false); + + /** + * 搜索状态变化监听 + */ + private List listeners = new ArrayList<>(); + + private TemplateTreeSearchManager() { + init(); + } + + private void init() { + this.treeSearchStatus = TreeSearchStatus.NOT_IN_SEARCH_MODE; + } + + private static class Holder { + private static final TemplateTreeSearchManager INSTANCE = new TemplateTreeSearchManager(); + } + + public static TemplateTreeSearchManager getInstance() { + return TemplateTreeSearchManager.Holder.INSTANCE; + } + + public TreeSearchStatus getTreeSearchStatus() { + return treeSearchStatus; + } + + public void setTreeSearchStatus(TreeSearchStatus treeSearchStatus) { + this.treeSearchStatus = treeSearchStatus; + // 每次设置搜索状态,都触发下监听,让页面跟随变化 + SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + for (TreeSearchStatusChangeListener listener : listeners) { + listener.updateTreeSearchChange(new TreeSearchStatusChangeEvent(treeSearchStatus)); + } + } + }); + } + + public void registerTreeSearchStatusChangeListener(TreeSearchStatusChangeListener listener) { + listeners.add(listener); + } + + /** + * 工具栏处切换到搜索面板 + * + * @param templateFileTree + */ + public void switchToSearch(TemplateFileTree templateFileTree) { + setTreeSearchStatus(TreeSearchStatus.SEARCH_NOT_BEGIN); + rendererHelper = new TreeSearchRendererHelper(); + rendererHelper.save(templateFileTree.getFileTreeCellRenderer()); + treeSearcher = new TemplateTreeSearcher(); + FineLoggerFactory.getLogger().debug("switch to template search"); + treeSearcher.beforeSearch(templateFileTree); + } + + /** + * 获取当前的模板树 + * + * @return + */ + private TemplateFileTree getCurrentTemplateTree() { + return TemplateTreePane.getInstance().getTemplateFileTree(); + } + + public boolean isMatchSetsEmpty() { + return treeSearcher.isMatchSetsEmpty(); + } + + /** + * 开始搜索 + * + * @param searchText + */ + public void startSearch(String searchText) { + if (isRepeatSearch(searchText) || StringUtils.isEmpty(searchText)) { + return; + } + setTreeSearchStatus(TreeSearchStatus.SEARCHING); + rendererHelper.replaceTreeRenderer(getCurrentTemplateTree(), searchText); + FineLoggerFactory.getLogger().debug("start template search for search text: {}", searchText); + treeSearcher.startSearch(searchText); + } + + /** + * 中断搜索 + */ + public void stopSearch() { + setTreeSearchStatus(TreeSearchStatus.SEARCH_STOPPED); + FineLoggerFactory.getLogger().debug("stop template search for search text: {}", lastSearchText); + treeSearcher.stopSearch(); + } + + /** + * 搜索完成 + */ + public void completeSearch() { + setTreeSearchStatus(TreeSearchStatus.SEARCH_COMPLETED); + setRefreshing(false); + FineLoggerFactory.getLogger().debug("complete template search for search text: {}", lastSearchText); + treeSearcher.completeSearch(); + } + + /** + * 刷新树,更新搜索的结果 + */ + public void updateTemplateTree() { + getCurrentTemplateTree().refresh4TreeSearch(); + } + + private boolean isRepeatSearch(String searchText) { + boolean repeat = StringUtils.equals(lastSearchText, searchText); + lastSearchText = searchText; + return repeat; + } + + /** + * 切换回工具栏,恢复数据集树UI + */ + public void restoreToolBarAndTreePane() { + setTreeSearchStatus(TreeSearchStatus.NOT_IN_SEARCH_MODE); + FineLoggerFactory.getLogger().info("out of template search"); + if (treeSearcher != null) { + treeSearcher.afterSearch(); + } + lastSearchText = null; + if (rendererHelper != null) { + rendererHelper.restore(getCurrentTemplateTree()); + } + } + + /** + * 恢复文件树UI + */ + public void restoreTreePane() { + setTreeSearchStatus(TreeSearchStatus.SEARCH_NOT_BEGIN); + lastSearchText = null; + if (rendererHelper != null) { + rendererHelper.restore(getCurrentTemplateTree()); + } + } + + /** + * 所有匹配的目录节点 + * @return + */ + public FileNode[] matchesNode() { + if (treeSearcher.getMatchSets().size() == 0) { + return new FileNode[0]; + } + return treeSearcher.getMatchSets().toArray(new FileNode[0]); + } + + public Map allFileNodes() { + return treeSearcher.getAllTemplates(); + } + + public boolean isInSearchMode() { + return getTreeSearchStatus() != TreeSearchStatus.NOT_IN_SEARCH_MODE; + } + + public void outOfSearchMode() { + restoreToolBarAndTreePane(); + } + + public boolean isRefreshing() { + return isRefreshing.get(); + } + + public void setRefreshing(boolean isRefreshing) { + this.isRefreshing.set(isRefreshing); + } +} diff --git a/designer-base/src/main/java/com/fr/design/mainframe/manager/search/searcher/TemplateDirTreeSearcher.java b/designer-base/src/main/java/com/fr/design/mainframe/manager/search/searcher/TemplateDirTreeSearcher.java new file mode 100644 index 000000000..5c553c8a6 --- /dev/null +++ b/designer-base/src/main/java/com/fr/design/mainframe/manager/search/searcher/TemplateDirTreeSearcher.java @@ -0,0 +1,164 @@ +package com.fr.design.mainframe.manager.search.searcher; + +import com.fr.concurrent.NamedThreadFactory; +import com.fr.design.gui.itree.filetree.TemplateFileTree; +import com.fr.design.mainframe.manager.search.TemplateDirTreeSearchManager; +import com.fr.design.mainframe.manager.search.searcher.control.common.TemplateDirSearchCallBack; +import com.fr.design.mainframe.manager.search.searcher.control.common.TemplateSearchTask; +import com.fr.design.mainframe.manager.search.searcher.control.pre.TemplateDirPreSearchTask; +import com.fr.design.mainframe.manager.search.searcher.control.pre.TemplatePreSearchCallBack; +import com.fr.design.search.TreeSearchStatus; +import com.fr.file.filetree.FileNode; +import com.fr.log.FineLoggerFactory; +import com.fr.stable.project.ProjectConstants; + +import java.util.Arrays; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.stream.Collectors; + + +public class TemplateDirTreeSearcher extends TemplateTreeSearcher { + + private ExecutorService executorService; + + private final Map allDirs = new ConcurrentHashMap<>(); + + private final AtomicInteger outLayerDirCount = new AtomicInteger(0); + + private final Set notCalculatedSets = new HashSet<>(); + + private final Set calculatedSets = new HashSet<>(); + + private final Set matchSets = new HashSet<>(); + + private final Object lock = new Object(); + + public Set getMatchSets() { + return this.matchSets; + } + + public boolean isMatchSetsEmpty() { + return matchSets.isEmpty(); + } + + public int getAllDirSize() { + return allDirs.size(); + } + + public Map getAllTemplates() { + return allDirs; + } + + public int getCalculatedCount() { + return this.calculatedSets.size(); + } + + public synchronized void addToCalculatedSets(List templateNames) { + for (String templateName : templateNames) { + FileNode fileNode = allDirs.get(templateName); + if (fileNode == null) { + return; + } + calculatedSets.add(templateName); + } + } + + public synchronized void addToMatchSets(List matchNodes) { + matchSets.addAll(matchNodes); + } + + public void addToNotCalculatedSets(List fileNodes) { + synchronized (lock) { + Map chileMap = fileNodes.stream().collect(Collectors.toMap(FileNode::getEnvPath, treeNode -> treeNode)); + notCalculatedSets.addAll(chileMap.keySet()); + allDirs.putAll(chileMap); + outLayerDirCount.decrementAndGet(); + lock.notify(); + } + } + + /** + * 正式搜索前,预加载一下每个最外层目录里面的所有子节点 + * + */ + public void beforeSearch(TemplateFileTree templateFileTree) { + executorService = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors(), new NamedThreadFactory(TemplateDirTreeSearcher.class)); + collectOutLayerTemplate(templateFileTree); + preCalculateChild(); + } + + protected void preCalculateChild() { + for (String notCalculatedNode : notCalculatedSets) { + FileNode fileNode = allDirs.get(notCalculatedNode); + //计算最外层目录下的所有子节点 + if (TemplateDirTreeSearchManager.getInstance().getTreeSearchStatus() == TreeSearchStatus.SEARCH_NOT_BEGIN && fileNode.isDirectory()) { + executorService.execute(new TemplateDirPreSearchTask(new TemplatePreSearchCallBack(this), fileNode)); + } + } + } + + /** + * 收集下此次搜索需要进行取数最外层的FileNode + * + * @param templateFileTree + */ + private void collectOutLayerTemplate(TemplateFileTree templateFileTree) { + FileNode[] fileNodes = templateFileTree.listFile(ProjectConstants.REPORTLETS_NAME); + Map fileNodeMap = Arrays.stream(fileNodes).collect(Collectors.toMap(FileNode::getEnvPath, treeNode -> treeNode)); + outLayerDirCount.addAndGet(fileNodes.length); + notCalculatedSets.addAll(fileNodeMap.keySet()); + allDirs.putAll(fileNodeMap); + } + + @Override + public void startSearch(String searchText) { + reset(); + do { + synchronized (lock) { + if (notCalculatedSets.isEmpty()) { + try { + lock.wait(100); + } catch (InterruptedException e) { + FineLoggerFactory.getLogger().error(e.getMessage(), e); + } + } + for (String notCalculated : notCalculatedSets) { + FileNode fileNode = allDirs.get(notCalculated); + if (TemplateDirTreeSearchManager.getInstance().getTreeSearchStatus() == TreeSearchStatus.SEARCHING) { + executorService.execute(new TemplateSearchTask(searchText, fileNode, new TemplateDirSearchCallBack(this))); + } + } + FineLoggerFactory.getLogger().info("[Template Search] At this stage calculate size: {}.", notCalculatedSets.size()); + notCalculatedSets.clear(); + } + } while (outLayerDirCount.get() != 0); + } + + @Override + public void stopSearch() { + + } + + @Override + public void completeSearch() { + + } + + private void reset() { + matchSets.clear(); + calculatedSets.clear(); + notCalculatedSets.addAll(allDirs.keySet()); + } + + public void afterSearch() { + allDirs.clear(); + executorService.shutdownNow(); + } +} diff --git a/designer-base/src/main/java/com/fr/design/mainframe/manager/search/searcher/TemplateTreeSearcher.java b/designer-base/src/main/java/com/fr/design/mainframe/manager/search/searcher/TemplateTreeSearcher.java new file mode 100644 index 000000000..fd02cfddc --- /dev/null +++ b/designer-base/src/main/java/com/fr/design/mainframe/manager/search/searcher/TemplateTreeSearcher.java @@ -0,0 +1,163 @@ +package com.fr.design.mainframe.manager.search.searcher; + +import com.fr.concurrent.NamedThreadFactory; +import com.fr.design.mainframe.manager.search.TemplateTreeSearchManager; +import com.fr.design.mainframe.manager.search.searcher.control.common.TemplateSearchCallBack; +import com.fr.design.mainframe.manager.search.searcher.control.common.TemplateSearchTask; +import com.fr.design.mainframe.manager.search.searcher.control.pre.TemplatePreSearchCallBack; +import com.fr.design.mainframe.manager.search.searcher.control.pre.TemplatePreSearchTask; +import com.fr.design.search.TreeSearchStatus; +import com.fr.design.search.TreeSearcher; +import com.fr.design.gui.itree.filetree.TemplateFileTree; +import com.fr.file.filetree.FileNode; +import com.fr.log.FineLoggerFactory; +import com.fr.stable.project.ProjectConstants; + +import java.util.*; +import java.util.concurrent.*; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.stream.Collectors; + +public class TemplateTreeSearcher implements TreeSearcher { + + private ExecutorService executorService; + + private final Map allTemplates = new ConcurrentHashMap<>(); + + private final Object lock = new Object(); + + private final AtomicInteger outLayerDirCount = new AtomicInteger(0); + + private final Set calculatedSets = new HashSet<>(); + + private final Set notCalculatedSets = new HashSet<>(); + + private final Set matchSets = new HashSet<>(); + + public boolean isMatchSetsEmpty() { + return matchSets.isEmpty(); + } + + public int getAllTemplateSize() { + return allTemplates.size(); + } + + public Map getAllTemplates() { + return allTemplates; + } + + public Set getMatchSets() { + return this.matchSets; + } + + public int getCalculatedCount() { + return this.calculatedSets.size(); + } + + public int getOutLayerCount() { + return this.outLayerDirCount.get(); + } + + public synchronized void addToCalculatedSets(List templateNames) { + for (String templateName : templateNames) { + FileNode fileNode = allTemplates.get(templateName); + if (fileNode == null) { + return; + } + calculatedSets.add(templateName); + } + } + + public synchronized void addToMatchSets(List matchNodes) { + matchSets.addAll(matchNodes); + } + + public void addToNotCalculatedSets(List fileNodes) { + synchronized (lock) { + Map chileMap = fileNodes.stream().collect(Collectors.toMap(FileNode::getEnvPath, treeNode -> treeNode)); + notCalculatedSets.addAll(chileMap.keySet()); + allTemplates.putAll(chileMap); + outLayerDirCount.decrementAndGet(); + lock.notify(); + } + } + + /** + * 正式搜索前,预加载一下每个最外层目录里面的所有子节点 + * + */ + public void beforeSearch(TemplateFileTree templateFileTree) { + executorService = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors(), new NamedThreadFactory(TemplateTreeSearcher.class)); + collectOutLayerTemplate(templateFileTree); + preCalculateChild(); + } + + protected void preCalculateChild() { + for (String notCalculatedNode : notCalculatedSets) { + FileNode fileNode = allTemplates.get(notCalculatedNode); + //计算最外层目录下的所有子节点 + if (TemplateTreeSearchManager.getInstance().getTreeSearchStatus() == TreeSearchStatus.SEARCH_NOT_BEGIN && fileNode.isDirectory()) { + executorService.execute(new TemplatePreSearchTask(new TemplatePreSearchCallBack(this), fileNode)); + } + } + } + + /** + * 收集下此次搜索需要进行取数最外层的FileNode + * + * @param templateFileTree + */ + private void collectOutLayerTemplate(TemplateFileTree templateFileTree) { + FileNode[] fileNodes = templateFileTree.listFile(ProjectConstants.REPORTLETS_NAME); + Map fileNodeMap = Arrays.stream(fileNodes).collect(Collectors.toMap(FileNode::getEnvPath, treeNode -> treeNode)); + long dirCount = Arrays.stream(fileNodes).filter(FileNode::isDirectory).count(); + outLayerDirCount.addAndGet((int) dirCount); + notCalculatedSets.addAll(fileNodeMap.keySet()); + allTemplates.putAll(fileNodeMap); + } + + @Override + public void startSearch(String searchText) { + reset(); + do { + synchronized (lock) { + if (notCalculatedSets.isEmpty()) { + try { + lock.wait(100); + } catch (InterruptedException e) { + FineLoggerFactory.getLogger().error(e.getMessage(), e); + } + } + for (String notCalculated : notCalculatedSets) { + FileNode fileNode = allTemplates.get(notCalculated); + if (TemplateTreeSearchManager.getInstance().getTreeSearchStatus() == TreeSearchStatus.SEARCHING) { + executorService.execute(new TemplateSearchTask(searchText, fileNode, new TemplateSearchCallBack(this))); + } + } + FineLoggerFactory.getLogger().info("[Template Search] At this stage calculate size: {}.", notCalculatedSets.size()); + notCalculatedSets.clear(); + } + } while (outLayerDirCount.get() != 0); + } + + @Override + public void stopSearch() { + + } + + @Override + public void completeSearch() { + + } + + private void reset() { + matchSets.clear(); + calculatedSets.clear(); + notCalculatedSets.addAll(allTemplates.keySet()); + } + + public void afterSearch() { + allTemplates.clear(); + executorService.shutdownNow(); + } +} diff --git a/designer-base/src/main/java/com/fr/design/mainframe/manager/search/searcher/control/common/TemplateDirSearchCallBack.java b/designer-base/src/main/java/com/fr/design/mainframe/manager/search/searcher/control/common/TemplateDirSearchCallBack.java new file mode 100644 index 000000000..d163c9ec7 --- /dev/null +++ b/designer-base/src/main/java/com/fr/design/mainframe/manager/search/searcher/control/common/TemplateDirSearchCallBack.java @@ -0,0 +1,49 @@ +package com.fr.design.mainframe.manager.search.searcher.control.common; + +import com.fr.design.mainframe.manager.search.TemplateDirTreeSearchManager; +import com.fr.design.mainframe.manager.search.searcher.TemplateDirTreeSearcher; +import com.fr.design.search.TreeSearchStatus; +import com.fr.design.search.control.TreeSearchCallback; +import com.fr.design.search.control.TreeSearchResult; + +import javax.swing.SwingUtilities; + +public class TemplateDirSearchCallBack implements TreeSearchCallback { + + protected TemplateDirTreeSearcher treeSearcher; + + public TemplateDirSearchCallBack(TemplateDirTreeSearcher treeSearcher) { + this.treeSearcher = treeSearcher; + } + + @Override + public void done(TreeSearchResult treeSearchResult) { + if (TemplateDirTreeSearchManager.getInstance().getTreeSearchStatus() != TreeSearchStatus.SEARCHING) { + return; + } + if (treeSearchResult.isSuccess()) { + // 添加结果 + addToTreeSearcher(treeSearchResult); + } + if (treeSearcher.getCalculatedCount() == treeSearcher.getAllDirSize()) { + updateTemplateTree(); + } + } + + protected void updateTemplateTree() { + SwingUtilities.invokeLater(() -> { + if (TemplateDirTreeSearchManager.getInstance().getTreeSearchStatus() != TreeSearchStatus.SEARCHING) { + return; + } + TemplateDirTreeSearchManager.getInstance().updateTemplateTree(); + TemplateDirTreeSearchManager.getInstance().completeSearch(); + }); + } + + private void addToTreeSearcher(TreeSearchResult treeSearchResult) { + // 添加到已计算结果集 + treeSearcher.addToCalculatedSets(treeSearchResult.getAddToCalculated()); + // 添加到匹配结果集 + treeSearcher.addToMatchSets(((TemplateSearchResult)treeSearchResult).getAddToMatchNode()); + } +} diff --git a/designer-base/src/main/java/com/fr/design/mainframe/manager/search/searcher/control/common/TemplateSearchCallBack.java b/designer-base/src/main/java/com/fr/design/mainframe/manager/search/searcher/control/common/TemplateSearchCallBack.java new file mode 100644 index 000000000..89f906585 --- /dev/null +++ b/designer-base/src/main/java/com/fr/design/mainframe/manager/search/searcher/control/common/TemplateSearchCallBack.java @@ -0,0 +1,59 @@ +package com.fr.design.mainframe.manager.search.searcher.control.common; + +import com.fr.design.search.control.TreeSearchCallback; +import com.fr.design.search.control.TreeSearchResult; +import com.fr.design.search.TreeSearchStatus; +import com.fr.design.mainframe.manager.search.TemplateTreeSearchManager; +import com.fr.design.mainframe.manager.search.searcher.TemplateTreeSearcher; +import javax.swing.SwingUtilities; + +public class TemplateSearchCallBack implements TreeSearchCallback { + + protected TemplateTreeSearcher treeSearcher; + private static final int BATCH_SIZE = 500; + + public TemplateSearchCallBack(TemplateTreeSearcher treeSearcher) { + this.treeSearcher = treeSearcher; + } + + @Override + public void done(TreeSearchResult treeSearchResult) { + if (TemplateTreeSearchManager.getInstance().getTreeSearchStatus() != TreeSearchStatus.SEARCHING) { + return; + } + if (treeSearchResult.isSuccess()) { + // 添加结果 + addToTreeSearcher(treeSearchResult); + } + updateTemplateTree(); + } + + protected void updateTemplateTree() { + int calculatedCount = treeSearcher.getCalculatedCount(); + boolean allCalculated = calculatedCount == treeSearcher.getAllTemplateSize(); + boolean isFinish = allCalculated && treeSearcher.getOutLayerCount() == 0; + if (calculatedCount % BATCH_SIZE == 0 || allCalculated) { + // 更新UI + updateOrFinish(isFinish); + } + } + + protected void updateOrFinish(boolean isFinished) { + SwingUtilities.invokeLater(() -> { + if (TemplateTreeSearchManager.getInstance().getTreeSearchStatus() != TreeSearchStatus.SEARCHING) { + return; + } + TemplateTreeSearchManager.getInstance().updateTemplateTree(); + if (isFinished) { + TemplateTreeSearchManager.getInstance().completeSearch(); + } + }); + } + + protected void addToTreeSearcher(TreeSearchResult treeSearchResult) { + // 添加到已计算结果集 + treeSearcher.addToCalculatedSets(treeSearchResult.getAddToCalculated()); + // 添加到匹配结果集 + treeSearcher.addToMatchSets(((TemplateSearchResult)treeSearchResult).getAddToMatchNode()); + } +} diff --git a/designer-base/src/main/java/com/fr/design/mainframe/manager/search/searcher/control/common/TemplateSearchResult.java b/designer-base/src/main/java/com/fr/design/mainframe/manager/search/searcher/control/common/TemplateSearchResult.java new file mode 100644 index 000000000..512fc6d83 --- /dev/null +++ b/designer-base/src/main/java/com/fr/design/mainframe/manager/search/searcher/control/common/TemplateSearchResult.java @@ -0,0 +1,127 @@ +package com.fr.design.mainframe.manager.search.searcher.control.common; + +import com.fr.design.search.control.TreeSearchResult; +import com.fr.file.filetree.FileNode; + +import java.util.ArrayList; +import java.util.List; + +public class TemplateSearchResult implements TreeSearchResult { + + private boolean success; + + private List addToExpand; + + private List addToCalculated; + + private List addToNotCalculated; + + private List addToMatchNode; + + protected TemplateSearchResult(TemplateSearchResult.Builder builder) { + this.success = builder.success; + this.addToMatchNode = builder.addToMatchNode; + this.addToExpand = builder.addToExpand; + this.addToCalculated = builder.addToCalculated; + this.addToNotCalculated = builder.addToNotCalculated; + } + + public void setSuccess(boolean success) { + this.success = success; + } + + public void setAddToMatchNode(List addToMatchNode) { + this.addToMatchNode = addToMatchNode; + } + + public void setAddToExpand(List addToExpand) { + this.addToExpand = addToExpand; + } + + public void setAddToCalculated(List addToCalculated) { + this.addToCalculated = addToCalculated; + } + + public void setAddToNotCalculated(List addToNotCalculated) { + this.addToNotCalculated = addToNotCalculated; + } + + @Override + public boolean isSuccess() { + return this.success; + } + + @Override + public List getAddToMatch() { + return new ArrayList<>(); + } + + public List getAddToMatchNode() { + return addToMatchNode; + } + + @Override + public List getAddToExpand() { + return this.addToExpand; + } + + @Override + public List getAddToCalculated() { + return this.addToCalculated; + } + + public List getAddToNotCalculated() { + return this.addToNotCalculated; + } + + public static class Builder { + + private boolean success; + + private List addToMatchNode; + + private List addToExpand; + + private List addToCalculated; + + private List addToNotCalculated; + + + public Builder() { + this.success = false; + this.addToMatchNode = new ArrayList<>(); + this.addToExpand = new ArrayList<>(); + this.addToCalculated = new ArrayList<>(); + this.addToNotCalculated = new ArrayList<>(); + } + + public TemplateSearchResult.Builder buildSuccess(boolean success) { + this.success = success; + return this; + } + + public TemplateSearchResult.Builder buildAddToMatch(List addToMatch) { + this.addToMatchNode = addToMatch; + return this; + } + + public TemplateSearchResult.Builder buildAddToExpand(List addToExpand) { + this.addToExpand = addToExpand; + return this; + } + + public TemplateSearchResult.Builder buildAddToCalculated(List addToCalculated) { + this.addToCalculated = addToCalculated; + return this; + } + + public TemplateSearchResult.Builder buildAddToNotCalculated(List addToNotCalculated) { + this.addToNotCalculated = addToNotCalculated; + return this; + } + + public TemplateSearchResult build() { + return new TemplateSearchResult(this); + } + } +} diff --git a/designer-base/src/main/java/com/fr/design/mainframe/manager/search/searcher/control/common/TemplateSearchTask.java b/designer-base/src/main/java/com/fr/design/mainframe/manager/search/searcher/control/common/TemplateSearchTask.java new file mode 100644 index 000000000..42aacc41d --- /dev/null +++ b/designer-base/src/main/java/com/fr/design/mainframe/manager/search/searcher/control/common/TemplateSearchTask.java @@ -0,0 +1,75 @@ +package com.fr.design.mainframe.manager.search.searcher.control.common; + +import com.fr.design.search.control.TreeSearchCallback; +import com.fr.design.search.control.TreeSearchResult; +import com.fr.design.search.control.TreeSearchTask; +import com.fr.file.filetree.FileNode; +import com.fr.log.FineLoggerFactory; + +import java.util.ArrayList; +import java.util.Arrays; + +public class TemplateSearchTask implements TreeSearchTask { + + /** + * 用户搜索的文本 + */ + private String searchText; + + /** + * 目录节点 + */ + private FileNode fileNode; + + private TreeSearchCallback callback; + + public TemplateSearchTask(String searchText, FileNode fileNode, TreeSearchCallback callback) { + this.searchText = searchText; + this.fileNode = fileNode; + this.callback = callback; + } + + @Override + public void run() { + TreeSearchResult result; + try { + result = dealWithTemplateWrapper(fileNode); + FineLoggerFactory.getLogger().debug("calculate template: {} succeeded", fileNode.getName()); + } catch (Throwable e) { + FineLoggerFactory.getLogger().error(e, "calculate template: {} failed", fileNode.getName()); + result = dealWithErrorTemplateWrapper(fileNode); + } + callback.done(result); + } + + private TreeSearchResult dealWithTemplateWrapper(FileNode fileNode) { + String fileName = fileNode.getName(); + + boolean isNameMatch = isMatchSearch(fileName, searchText); + return new TemplateSearchResult.Builder() + .buildSuccess(true) + .buildAddToMatch(isNameMatch ? Arrays.asList(fileNode) : new ArrayList<>()) + .buildAddToExpand(fileNode.isDirectory() ? Arrays.asList(fileName) : new ArrayList<>()) + .buildAddToCalculated(Arrays.asList(fileNode.getEnvPath())) + .build(); + } + + /** + * 处理错误情况 + * + * @param fileNode + */ + private TreeSearchResult dealWithErrorTemplateWrapper(FileNode fileNode) { + return new TemplateSearchResult.Builder().buildSuccess(false).build(); + } + + /** + * 判断是否匹配搜索文本,不区分大小写 + * + * @param str + * @return + */ + private boolean isMatchSearch(String str, String searchText) { + return str.toUpperCase().contains(searchText.toUpperCase()); + } +} diff --git a/designer-base/src/main/java/com/fr/design/mainframe/manager/search/searcher/control/pane/TemplateDirSearchRemindPane.java b/designer-base/src/main/java/com/fr/design/mainframe/manager/search/searcher/control/pane/TemplateDirSearchRemindPane.java new file mode 100644 index 000000000..a0474333b --- /dev/null +++ b/designer-base/src/main/java/com/fr/design/mainframe/manager/search/searcher/control/pane/TemplateDirSearchRemindPane.java @@ -0,0 +1,211 @@ +package com.fr.design.mainframe.manager.search.searcher.control.pane; + +import com.fr.base.svg.IconUtils; +import com.fr.design.constants.UIConstants; +import com.fr.design.gui.itree.filetree.EnvFileTree; +import com.fr.design.mainframe.manager.search.TemplateDirTreeSearchManager; +import com.fr.design.search.event.TreeSearchStatusChangeEvent; +import com.fr.design.search.event.TreeSearchStatusChangeListener; +import com.fr.design.search.TreeSearchStatus; +import com.fr.design.gui.icontainer.UIScrollPane; +import com.fr.design.gui.ilable.UILabel; +import com.fr.design.i18n.Toolkit; +import com.fr.design.layout.FRGUIPaneFactory; +import javax.swing.BorderFactory; +import javax.swing.JPanel; +import javax.swing.SwingConstants; +import java.awt.BorderLayout; +import java.awt.CardLayout; +import java.awt.Color; +import java.awt.Dimension; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.awt.event.MouseListener; +import java.awt.FlowLayout; + +public class TemplateDirSearchRemindPane extends JPanel implements TreeSearchStatusChangeListener { + + private TemplateDirSearchRemindPane.RemindPane remindPane; + private TemplateDirSearchRemindPane.TreePane treePane; + + public TemplateDirSearchRemindPane(EnvFileTree templateFileTree) { + this.setLayout(FRGUIPaneFactory.createBorderLayout()); + remindPane = new TemplateDirSearchRemindPane.RemindPane(); + treePane = new TemplateDirSearchRemindPane.TreePane(templateFileTree); + // 初始状态 + this.add(remindPane, BorderLayout.NORTH); + this.add(treePane, BorderLayout.CENTER); + TemplateDirTreeSearchManager.getInstance().registerTreeSearchStatusChangeListener(this); + } + + /** + * 根据搜索状态变化,来调整自身面板的显示 + * + * @param event + */ + @Override + public void updateTreeSearchChange(TreeSearchStatusChangeEvent event) { + TreeSearchStatus status = event.getTreeSearchStatus(); + if (status == TreeSearchStatus.SEARCH_NOT_BEGIN || status == TreeSearchStatus.NOT_IN_SEARCH_MODE) { + remindPane.onNotBegin(); + treePane.onNotBegin(); + } else if (status == TreeSearchStatus.SEARCHING) { + remindPane.onInSearching(); + treePane.onInSearching(); + } else if (status == TreeSearchStatus.SEARCH_STOPPED) { + remindPane.onStoppedSearching(); + treePane.onStoppedSearching(); + } else { + boolean matchSetsEmpty = TemplateDirTreeSearchManager.getInstance().isMatchSetsEmpty(); + // 代表是否搜索出结果 + remindPane.onDoneSearching(matchSetsEmpty); + treePane.onDoneSearching(matchSetsEmpty); + } + this.revalidate(); + } + + private interface TreeSearchStatusChange { + + void onNotBegin(); + + void onInSearching(); + + void onStoppedSearching(); + + void onDoneSearching(boolean matchSetsEmpty); + } + + private class TreePane extends JPanel implements TemplateDirSearchRemindPane.TreeSearchStatusChange { + + private UIScrollPane scrollPane; + + private JPanel notFoundPane; + + private CardLayout cardLayout; + + private static final String SCROLL_PANE = "scrollPane"; + + private static final String NOT_FOUND_PANE = "notFoundPane"; + + public TreePane(EnvFileTree templateFileTree) { + init(templateFileTree); + } + + private void init(EnvFileTree templateFileTree) { + + scrollPane = new UIScrollPane(templateFileTree); + scrollPane.setBorder(null); + + notFoundPane = FRGUIPaneFactory.createVerticalFlowLayout_Pane(true, FlowLayout.LEADING, 0, 5); + UILabel emptyPicLabel = new UILabel(); + emptyPicLabel.setIcon(IconUtils.readIcon("com/fr/base/images/share/no_match_icon.png")); + emptyPicLabel.setHorizontalAlignment(SwingConstants.CENTER); + emptyPicLabel.setPreferredSize(new Dimension(240, 100)); + UILabel textLabel = new UILabel(com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Tree_Search_Not_Match"), SwingConstants.CENTER); + textLabel.setForeground(Color.gray); + textLabel.setHorizontalAlignment(SwingConstants.CENTER); + textLabel.setPreferredSize(new Dimension(240, 20)); + notFoundPane.add(emptyPicLabel); + notFoundPane.add(textLabel); + notFoundPane.setBorder(BorderFactory.createEmptyBorder(80, 0, 0, 0)); + + cardLayout = new CardLayout(); + this.setLayout(cardLayout); + this.add(scrollPane, SCROLL_PANE); + this.add(notFoundPane, NOT_FOUND_PANE); + cardLayout.show(this, SCROLL_PANE); + } + + @Override + public void onNotBegin() { + switchPane(SCROLL_PANE); + } + + @Override + public void onInSearching() { + switchPane(SCROLL_PANE); + } + + @Override + public void onStoppedSearching() { + switchPane(SCROLL_PANE); + } + + @Override + public void onDoneSearching(boolean matchSetsEmpty) { + if (matchSetsEmpty) { + switchPane(NOT_FOUND_PANE); + } + } + + private void switchPane(String paneName) { + cardLayout.show(this, paneName); + } + } + + private static class RemindPane extends JPanel implements TemplateDirSearchRemindPane.TreeSearchStatusChange { + + private static final String IN_SEARCHING = com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Tree_Search_In_Searching"); + private static final String STOP_SEARCHING = com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Tree_Search_Stop_Search"); + private static final String SEARCHING_STOPPED = com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Tree_Search_Search_Stopped"); + private static final String DONE_SEARCHING = Toolkit.i18nText("Fine-Design_Tree_Search_Search_Completed"); + + private UILabel textLabel; + + private UILabel stopLabel; + + private MouseListener stopSearch; + + public RemindPane() { + init(); + } + + private void init() { + this.setLayout(new FlowLayout(FlowLayout.LEFT, 10, 0)); + // 初始情况下为Not_Begin + textLabel = new UILabel(); + textLabel.setForeground(Color.gray); + stopLabel = new UILabel(); + stopLabel.setForeground(UIConstants.NORMAL_BLUE); + stopSearch = new MouseAdapter() { + @Override + public void mouseClicked(MouseEvent e) { + TemplateDirTreeSearchManager.getInstance().stopSearch(); + } + }; + stopLabel.addMouseListener(stopSearch); + this.add(textLabel); + this.add(stopLabel); + onNotBegin(); + } + + @Override + public void onNotBegin() { + this.setVisible(false); + } + + @Override + public void onInSearching() { + this.textLabel.setVisible(false); + this.stopLabel.setText(STOP_SEARCHING); + this.stopLabel.setVisible(true); + this.setVisible(true); + } + + @Override + public void onStoppedSearching() { + this.textLabel.setText(SEARCHING_STOPPED); + this.textLabel.setVisible(true); + this.stopLabel.setVisible(false); + this.setVisible(true); + } + + @Override + public void onDoneSearching(boolean matchSetsEmpty) { + this.textLabel.setText(DONE_SEARCHING); + this.textLabel.setVisible(true); + this.stopLabel.setVisible(false); + this.setVisible(true); + } + } +} diff --git a/designer-base/src/main/java/com/fr/design/mainframe/manager/search/searcher/control/pane/TemplateDirTreeSearchPane.java b/designer-base/src/main/java/com/fr/design/mainframe/manager/search/searcher/control/pane/TemplateDirTreeSearchPane.java new file mode 100644 index 000000000..18af8a742 --- /dev/null +++ b/designer-base/src/main/java/com/fr/design/mainframe/manager/search/searcher/control/pane/TemplateDirTreeSearchPane.java @@ -0,0 +1,149 @@ +package com.fr.design.mainframe.manager.search.searcher.control.pane; + +import com.fr.base.svg.IconUtils; +import com.fr.design.constants.UIConstants; +import com.fr.design.file.TemplateDirTreePane; +import com.fr.design.gui.ilable.UILabel; +import com.fr.design.gui.itextfield.UITextField; +import com.fr.design.i18n.Toolkit; +import com.fr.design.layout.FRGUIPaneFactory; +import com.fr.design.mainframe.manager.search.TemplateDirTreeSearchManager; +import com.fr.design.search.event.TreeSearchStatusChangeEvent; +import com.fr.design.search.event.TreeSearchStatusChangeListener; +import com.fr.stable.StringUtils; + +import javax.swing.BorderFactory; +import javax.swing.JPanel; +import javax.swing.event.DocumentEvent; +import javax.swing.event.DocumentListener; +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.Insets; +import java.awt.event.FocusEvent; +import java.awt.event.FocusListener; +import java.awt.event.KeyAdapter; +import java.awt.event.KeyEvent; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; + +public class TemplateDirTreeSearchPane extends JPanel implements TreeSearchStatusChangeListener { + + /** + * 搜索输入框 + */ + private UITextField searchTextField; + + /** + * 搜索面板 + */ + private JPanel searchPane; + + private final KeyAdapter enterPressed = new KeyAdapter() { + @Override + public void keyPressed(KeyEvent e) { + if (e.getKeyCode() == KeyEvent.VK_ENTER) { + TemplateDirTreeSearchManager.getInstance().startSearch(searchTextField.getText()); + } + } + }; + + public TemplateDirTreeSearchPane() { + this.setLayout(FRGUIPaneFactory.createBorderLayout()); + this.setBorder(BorderFactory.createEmptyBorder(10, 15, 0, 10)); + initSearchPane(); + add(searchPane, BorderLayout.CENTER); + TemplateDirTreeSearchManager.getInstance().registerTreeSearchStatusChangeListener(this); + TemplateDirTreePane.getInstance().refreshDockingView(); + TemplateDirTreeSearchManager.getInstance().beforeSearch(TemplateDirTreePane.getInstance().getTemplateDirTree()); + } + + private void initSearchPane() { + searchPane = new JPanel(FRGUIPaneFactory.createBorderLayout()); + searchPane.setBorder(BorderFactory.createLineBorder(UIConstants.TOOLBAR_BORDER_COLOR)); + searchPane.setBackground(Color.WHITE); + // 左侧搜索图标 + UILabel searchLabel = new UILabel(IconUtils.readIcon("/com/fr/design/images/data/search")); + searchLabel.setBorder(BorderFactory.createEmptyBorder(0, 12, 0, 0)); + searchLabel.addMouseListener(new MouseAdapter() { + @Override + public void mouseClicked(MouseEvent e) { + // do nothing + } + }); + + // 中间输入框 + initSearchTextField(); + + // 右侧返回图标 + UILabel returnLabel = new UILabel(IconUtils.readIcon("/com/fr/design/images/data/clear")); + returnLabel.setToolTipText(Toolkit.i18nText("Fine-Design_Tree_Search_Return")); + returnLabel.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 11)); + returnLabel.addMouseListener(new MouseAdapter() { + @Override + public void mouseClicked(MouseEvent e) { + TemplateDirTreeSearchManager.getInstance().outOfSearchMode(); + TemplateDirTreePane.getInstance().refreshDockingView(); + } + }); + + searchPane.add(searchLabel, BorderLayout.WEST); + searchPane.add(searchTextField, BorderLayout.CENTER); + searchPane.add(returnLabel, BorderLayout.EAST); + } + + private void initSearchTextField() { + searchTextField = new UITextField(){ + @Override + public Insets getInsets() { + return new Insets(2, 4, 0, 4); + } + }; + searchTextField.setBorderPainted(false); + searchTextField.setPlaceholder(com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Template_Search_Press_Enter_For_Search")); + searchTextField.addFocusListener(new FocusListener() { + @Override + public void focusGained(FocusEvent e) { + searchPane.setBorder(BorderFactory.createLineBorder(UIConstants.NORMAL_BLUE)); + searchPane.repaint(); + } + + @Override + public void focusLost(FocusEvent e) { + searchPane.setBorder(BorderFactory.createLineBorder(UIConstants.TOOLBAR_BORDER_COLOR)); + searchPane.repaint(); + } + }); + this.searchTextField.getDocument().addDocumentListener(new DocumentListener() { + @Override + public void insertUpdate(DocumentEvent e) { + } + + @Override + public void removeUpdate(DocumentEvent e) { + dealWithTextChange(); + } + + @Override + public void changedUpdate(DocumentEvent e) { + } + }); + this.searchTextField.addKeyListener(enterPressed); + } + + private void dealWithTextChange() { + if (StringUtils.isEmpty(searchTextField.getText()) && TemplateDirTreeSearchManager.getInstance().isInSearchMode()) { + // 如果是搜索模式下,看作是用户删除输入框文字,仅复原TemplateTreePane + TemplateDirTreeSearchManager.getInstance().restoreTreePane(); + TemplateDirTreePane.getInstance().refreshDockingView(); + } + } + + /** + * 目录树不涉及到工具栏和搜索栏的切换,无需实现 + * @param event + */ + @Override + public void updateTreeSearchChange(TreeSearchStatusChangeEvent event) { + + } +} diff --git a/designer-base/src/main/java/com/fr/design/mainframe/manager/search/searcher/control/pane/TemplateSearchRemindPane.java b/designer-base/src/main/java/com/fr/design/mainframe/manager/search/searcher/control/pane/TemplateSearchRemindPane.java new file mode 100644 index 000000000..c2d53c82b --- /dev/null +++ b/designer-base/src/main/java/com/fr/design/mainframe/manager/search/searcher/control/pane/TemplateSearchRemindPane.java @@ -0,0 +1,212 @@ +package com.fr.design.mainframe.manager.search.searcher.control.pane; + +import com.fr.base.svg.IconUtils; +import com.fr.design.constants.UIConstants; +import com.fr.design.gui.itree.filetree.EnvFileTree; +import com.fr.design.search.event.TreeSearchStatusChangeEvent; +import com.fr.design.search.event.TreeSearchStatusChangeListener; +import com.fr.design.search.TreeSearchStatus; +import com.fr.design.gui.icontainer.UIScrollPane; +import com.fr.design.gui.ilable.UILabel; +import com.fr.design.i18n.Toolkit; +import com.fr.design.layout.FRGUIPaneFactory; +import com.fr.design.mainframe.manager.search.TemplateTreeSearchManager; + +import javax.swing.BorderFactory; +import javax.swing.JPanel; +import javax.swing.SwingConstants; +import java.awt.BorderLayout; +import java.awt.CardLayout; +import java.awt.Color; +import java.awt.Dimension; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.awt.event.MouseListener; +import java.awt.FlowLayout; + +public class TemplateSearchRemindPane extends JPanel implements TreeSearchStatusChangeListener { + + private TemplateSearchRemindPane.RemindPane remindPane; + private TemplateSearchRemindPane.TreePane treePane; + + public TemplateSearchRemindPane(EnvFileTree templateFileTree) { + this.setLayout(FRGUIPaneFactory.createBorderLayout()); + remindPane = new TemplateSearchRemindPane.RemindPane(); + treePane = new TemplateSearchRemindPane.TreePane(templateFileTree); + // 初始状态 + this.add(remindPane, BorderLayout.NORTH); + this.add(treePane, BorderLayout.CENTER); + TemplateTreeSearchManager.getInstance().registerTreeSearchStatusChangeListener(this); + } + + /** + * 根据搜索状态变化,来调整自身面板的显示 + * + * @param event + */ + @Override + public void updateTreeSearchChange(TreeSearchStatusChangeEvent event) { + TreeSearchStatus status = event.getTreeSearchStatus(); + if (status == TreeSearchStatus.SEARCH_NOT_BEGIN || status == TreeSearchStatus.NOT_IN_SEARCH_MODE) { + remindPane.onNotBegin(); + treePane.onNotBegin(); + } else if (status == TreeSearchStatus.SEARCHING) { + remindPane.onInSearching(); + treePane.onInSearching(); + } else if (status == TreeSearchStatus.SEARCH_STOPPED) { + remindPane.onStoppedSearching(); + treePane.onStoppedSearching(); + } else { + boolean matchSetsEmpty = TemplateTreeSearchManager.getInstance().isMatchSetsEmpty(); + // 代表是否搜索出结果 + remindPane.onDoneSearching(matchSetsEmpty); + treePane.onDoneSearching(matchSetsEmpty); + } + this.revalidate(); + } + + private interface TreeSearchStatusChange { + + void onNotBegin(); + + void onInSearching(); + + void onStoppedSearching(); + + void onDoneSearching(boolean matchSetsEmpty); + } + + private class TreePane extends JPanel implements TemplateSearchRemindPane.TreeSearchStatusChange { + + private UIScrollPane scrollPane; + + private JPanel notFoundPane; + + private CardLayout cardLayout; + + private static final String SCROLL_PANE = "scrollPane"; + + private static final String NOT_FOUND_PANE = "notFoundPane"; + + public TreePane(EnvFileTree templateFileTree) { + init(templateFileTree); + } + + private void init(EnvFileTree templateFileTree) { + + scrollPane = new UIScrollPane(templateFileTree); + scrollPane.setBorder(null); + + notFoundPane = FRGUIPaneFactory.createVerticalFlowLayout_Pane(true, FlowLayout.LEADING, 0, 5); + UILabel emptyPicLabel = new UILabel(); + emptyPicLabel.setIcon(IconUtils.readIcon("com/fr/base/images/share/no_match_icon.png")); + emptyPicLabel.setHorizontalAlignment(SwingConstants.CENTER); + emptyPicLabel.setPreferredSize(new Dimension(240, 100)); + UILabel textLabel = new UILabel(com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Tree_Search_Not_Match"), SwingConstants.CENTER); + textLabel.setForeground(Color.gray); + textLabel.setHorizontalAlignment(SwingConstants.CENTER); + textLabel.setPreferredSize(new Dimension(240, 20)); + notFoundPane.add(emptyPicLabel); + notFoundPane.add(textLabel); + notFoundPane.setBorder(BorderFactory.createEmptyBorder(80, 0, 0, 0)); + + cardLayout = new CardLayout(); + this.setLayout(cardLayout); + this.add(scrollPane, SCROLL_PANE); + this.add(notFoundPane, NOT_FOUND_PANE); + cardLayout.show(this, SCROLL_PANE); + } + + @Override + public void onNotBegin() { + switchPane(SCROLL_PANE); + } + + @Override + public void onInSearching() { + switchPane(SCROLL_PANE); + } + + @Override + public void onStoppedSearching() { + switchPane(SCROLL_PANE); + } + + @Override + public void onDoneSearching(boolean matchSetsEmpty) { + if (matchSetsEmpty) { + switchPane(NOT_FOUND_PANE); + } + } + + private void switchPane(String paneName) { + cardLayout.show(this, paneName); + } + } + + private static class RemindPane extends JPanel implements TemplateSearchRemindPane.TreeSearchStatusChange { + + private static final String IN_SEARCHING = com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Tree_Search_In_Searching"); + private static final String STOP_SEARCHING = com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Tree_Search_Stop_Search"); + private static final String SEARCHING_STOPPED = com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Tree_Search_Search_Stopped"); + private static final String DONE_SEARCHING = Toolkit.i18nText("Fine-Design_Tree_Search_Search_Completed"); + + private UILabel textLabel; + + private UILabel stopLabel; + + private MouseListener stopSearch; + + public RemindPane() { + init(); + } + + private void init() { + this.setLayout(new FlowLayout(FlowLayout.LEFT, 10, 0)); + // 初始情况下为Not_Begin + textLabel = new UILabel(); + textLabel.setForeground(Color.gray); + stopLabel = new UILabel(); + stopLabel.setForeground(UIConstants.NORMAL_BLUE); + stopSearch = new MouseAdapter() { + @Override + public void mouseClicked(MouseEvent e) { + TemplateTreeSearchManager.getInstance().stopSearch(); + } + }; + stopLabel.addMouseListener(stopSearch); + this.add(textLabel); + this.add(stopLabel); + onNotBegin(); + } + + @Override + public void onNotBegin() { + this.setVisible(false); + } + + @Override + public void onInSearching() { + this.textLabel.setVisible(false); + this.stopLabel.setText(STOP_SEARCHING); + this.stopLabel.setVisible(true); + this.setVisible(true); + } + + @Override + public void onStoppedSearching() { + this.textLabel.setText(SEARCHING_STOPPED); + this.textLabel.setVisible(true); + this.stopLabel.setVisible(false); + this.setVisible(true); + } + + @Override + public void onDoneSearching(boolean matchSetsEmpty) { + this.textLabel.setText(DONE_SEARCHING); + this.textLabel.setVisible(true); + this.stopLabel.setVisible(false); + this.setVisible(true); + } + } +} diff --git a/designer-base/src/main/java/com/fr/design/mainframe/manager/search/searcher/control/pane/TemplateTreeSearchToolbarPane.java b/designer-base/src/main/java/com/fr/design/mainframe/manager/search/searcher/control/pane/TemplateTreeSearchToolbarPane.java new file mode 100644 index 000000000..0ff8c0591 --- /dev/null +++ b/designer-base/src/main/java/com/fr/design/mainframe/manager/search/searcher/control/pane/TemplateTreeSearchToolbarPane.java @@ -0,0 +1,209 @@ +package com.fr.design.mainframe.manager.search.searcher.control.pane; + +import com.fr.base.svg.IconUtils; +import com.fr.design.constants.UIConstants; +import com.fr.design.search.event.TreeSearchStatusChangeEvent; +import com.fr.design.search.event.TreeSearchStatusChangeListener; +import com.fr.design.search.TreeSearchStatus; +import com.fr.design.file.TemplateTreePane; +import com.fr.design.gui.ilable.UILabel; +import com.fr.design.gui.itextfield.UITextField; +import com.fr.design.gui.itoolbar.UIToolbar; +import com.fr.design.i18n.Toolkit; +import com.fr.design.layout.FRGUIPaneFactory; +import com.fr.design.mainframe.manager.search.TemplateTreeSearchManager; +import com.fr.stable.StringUtils; + +import javax.swing.BorderFactory; +import javax.swing.JPanel; +import javax.swing.event.DocumentEvent; +import javax.swing.event.DocumentListener; +import java.awt.BorderLayout; +import java.awt.CardLayout; +import java.awt.Color; +import java.awt.Dimension; +import java.awt.Insets; +import java.awt.event.FocusEvent; +import java.awt.event.FocusListener; +import java.awt.event.KeyAdapter; +import java.awt.event.KeyEvent; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; + +public class TemplateTreeSearchToolbarPane extends JPanel implements TreeSearchStatusChangeListener { + + public static final String TOOLBAR_PANE = "toolbarPane"; + + public static final String SEARCH_PANE = "searchPane"; + + /** + * 工具栏 + */ + private UIToolbar toolbar; + + /** + * 工具栏面板 + */ + private JPanel toolbarPane; + + /** + * 搜索面板 + */ + private JPanel searchPane; + + /** + * 搜索输入框 + */ + private UITextField searchTextField; + + /** + * 内容面板 + */ + private JPanel contentPane; + + /** + * 卡片布局管理器 + */ + private CardLayout cardLayout; + + private final KeyAdapter enterPressed = new KeyAdapter() { + @Override + public void keyPressed(KeyEvent e) { + if (e.getKeyCode() == KeyEvent.VK_ENTER) { + TemplateTreeSearchManager.getInstance().startSearch(searchTextField.getText()); + } + } + }; + + public TemplateTreeSearchToolbarPane(UIToolbar toolbar) { + this.toolbar = toolbar; + this.setLayout(FRGUIPaneFactory.createBorderLayout()); + initToolbarPane(); + initSearchPane(); + initContentPane(); + add(contentPane, BorderLayout.CENTER); + setPreferredSize(new Dimension(240, 30)); + TemplateTreeSearchManager.getInstance().registerTreeSearchStatusChangeListener(this); + } + + private void initContentPane() { + cardLayout = new CardLayout(); + contentPane = new JPanel(cardLayout); + contentPane.add(searchPane, SEARCH_PANE); + contentPane.add(toolbarPane, TOOLBAR_PANE); + cardLayout.show(contentPane, TOOLBAR_PANE); + } + + private void initSearchPane() { + searchPane = new JPanel(FRGUIPaneFactory.createBorderLayout()); + searchPane.setBorder(BorderFactory.createLineBorder(UIConstants.TOOLBAR_BORDER_COLOR)); + searchPane.setBackground(Color.WHITE); + // 左侧搜索图标 + UILabel searchLabel = new UILabel(IconUtils.readIcon("/com/fr/design/images/data/search")); + searchLabel.setBorder(BorderFactory.createEmptyBorder(0, 12, 0, 0)); + searchLabel.addMouseListener(new MouseAdapter() { + @Override + public void mouseClicked(MouseEvent e) { + // do nothing + } + }); + // 中间输入框 + initSearchTextField(); + // 右侧返回图标 + UILabel returnLabel = new UILabel(IconUtils.readIcon("/com/fr/design/images/data/clear")); + returnLabel.setToolTipText(Toolkit.i18nText("Fine-Design_Tree_Search_Return")); + returnLabel.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 11)); + returnLabel.addMouseListener(new MouseAdapter() { + @Override + public void mouseClicked(MouseEvent e) { + TemplateTreeSearchManager.getInstance().outOfSearchMode(); + TemplateTreePane.getInstance().refreshDockingView(); + } + }); + + searchPane.add(searchLabel, BorderLayout.WEST); + searchPane.add(searchTextField, BorderLayout.CENTER); + searchPane.add(returnLabel, BorderLayout.EAST); + } + + private void initSearchTextField() { + searchTextField = new UITextField(){ + @Override + public Insets getInsets() { + return new Insets(2, 4, 0, 4); + } + }; + searchTextField.setBorderPainted(false); + searchTextField.setPlaceholder(com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Template_Search_Press_Enter_For_Search")); + searchTextField.addFocusListener(new FocusListener() { + @Override + public void focusGained(FocusEvent e) { + searchPane.setBorder(BorderFactory.createLineBorder(UIConstants.NORMAL_BLUE)); + searchPane.repaint(); + } + + @Override + public void focusLost(FocusEvent e) { + searchPane.setBorder(BorderFactory.createLineBorder(UIConstants.TOOLBAR_BORDER_COLOR)); + searchPane.repaint(); + } + }); + this.searchTextField.getDocument().addDocumentListener(new DocumentListener() { + @Override + public void insertUpdate(DocumentEvent e) { + } + + @Override + public void removeUpdate(DocumentEvent e) { + dealWithTextChange(); + } + + @Override + public void changedUpdate(DocumentEvent e) { + } + }); + this.searchTextField.addKeyListener(enterPressed); + } + + private void dealWithTextChange() { + if (StringUtils.isEmpty(searchTextField.getText()) && TemplateTreeSearchManager.getInstance().isInSearchMode()) { + // 如果是搜索模式下,看作是用户删除输入框文字,仅复原TemplateTreePane + TemplateTreeSearchManager.getInstance().restoreTreePane(); + TemplateTreePane.getInstance().refreshDockingView(); + } + } + + private void initToolbarPane() { + toolbarPane = new JPanel(); + toolbarPane.setLayout(FRGUIPaneFactory.createBorderLayout()); + toolbarPane.add(toolbar, BorderLayout.CENTER); + } + + /** + * 交换当前面板层级 + */ + public void switchPane(String name) { + cardLayout.show(contentPane, name); + } + + public void setPlaceHolder(String placeHolder) { + this.searchTextField.setPlaceholder(placeHolder); + } + + /** + * 根据搜索状态变化,来调整自身面板的显示 + * + * @param event + */ + @Override + public void updateTreeSearchChange(TreeSearchStatusChangeEvent event) { + TreeSearchStatus treeSearchStatus = event.getTreeSearchStatus(); + if (treeSearchStatus == TreeSearchStatus.NOT_IN_SEARCH_MODE) { + this.searchTextField.setText(StringUtils.EMPTY); + switchPane(TOOLBAR_PANE); + } else { + switchPane(SEARCH_PANE); + } + } + +} diff --git a/designer-base/src/main/java/com/fr/design/mainframe/manager/search/searcher/control/pre/TemplateDirPreSearchTask.java b/designer-base/src/main/java/com/fr/design/mainframe/manager/search/searcher/control/pre/TemplateDirPreSearchTask.java new file mode 100644 index 000000000..6ab778243 --- /dev/null +++ b/designer-base/src/main/java/com/fr/design/mainframe/manager/search/searcher/control/pre/TemplateDirPreSearchTask.java @@ -0,0 +1,32 @@ +package com.fr.design.mainframe.manager.search.searcher.control.pre; + +import com.fr.design.file.TemplateDirTreePane; +import com.fr.design.search.control.TreeSearchCallback; +import com.fr.file.filetree.FileNode; + +import java.util.List; + +public class TemplateDirPreSearchTask extends TemplatePreSearchTask { + + public TemplateDirPreSearchTask(TreeSearchCallback callback, FileNode fileNode) { + super(callback, fileNode); + } + + /** + * 加载最外层目录下所有的子节点 + * + * @param fileNodes + * @param fileNode + */ + protected void dealWithChildTemplate(List fileNodes, FileNode fileNode) { + FileNode[] childNodes = TemplateDirTreePane.getInstance().getTemplateDirTree().listFile(fileNode.getEnvPath()); + for (FileNode childNode : childNodes) { + if (childNode.isDirectory()) { + fileNodes.add(childNode); + dealWithChildTemplate(fileNodes, childNode); + } else { + fileNodes.add(childNode); + } + } + } +} diff --git a/designer-base/src/main/java/com/fr/design/mainframe/manager/search/searcher/control/pre/TemplatePreSearchCallBack.java b/designer-base/src/main/java/com/fr/design/mainframe/manager/search/searcher/control/pre/TemplatePreSearchCallBack.java new file mode 100644 index 000000000..e98b7f264 --- /dev/null +++ b/designer-base/src/main/java/com/fr/design/mainframe/manager/search/searcher/control/pre/TemplatePreSearchCallBack.java @@ -0,0 +1,26 @@ +package com.fr.design.mainframe.manager.search.searcher.control.pre; + +import com.fr.design.mainframe.manager.search.searcher.control.common.TemplateSearchCallBack; +import com.fr.design.mainframe.manager.search.searcher.control.common.TemplateSearchResult; +import com.fr.design.search.control.TreeSearchResult; +import com.fr.design.mainframe.manager.search.searcher.TemplateTreeSearcher; + +public class TemplatePreSearchCallBack extends TemplateSearchCallBack { + + public TemplatePreSearchCallBack(TemplateTreeSearcher treeSearcher) { + super(treeSearcher); + } + + @Override + public void done(TreeSearchResult searchResult) { + if (searchResult.isSuccess()) { + addToTreeSearcher(searchResult); + } + } + + @Override + protected void addToTreeSearcher(TreeSearchResult searchResult) { + TemplateSearchResult templateSearchResult = (TemplateSearchResult) searchResult; + treeSearcher.addToNotCalculatedSets(templateSearchResult.getAddToNotCalculated()); + } +} diff --git a/designer-base/src/main/java/com/fr/design/mainframe/manager/search/searcher/control/pre/TemplatePreSearchTask.java b/designer-base/src/main/java/com/fr/design/mainframe/manager/search/searcher/control/pre/TemplatePreSearchTask.java new file mode 100644 index 000000000..f50eea77c --- /dev/null +++ b/designer-base/src/main/java/com/fr/design/mainframe/manager/search/searcher/control/pre/TemplatePreSearchTask.java @@ -0,0 +1,58 @@ +package com.fr.design.mainframe.manager.search.searcher.control.pre; + +import com.fr.design.mainframe.manager.search.searcher.control.common.TemplateSearchResult; +import com.fr.design.search.control.TreeSearchCallback; +import com.fr.design.search.control.TreeSearchResult; +import com.fr.design.search.control.TreeSearchTask; +import com.fr.design.file.TemplateTreePane; +import com.fr.file.filetree.FileNode; +import com.fr.log.FineLoggerFactory; + +import java.util.ArrayList; +import java.util.List; + +public class TemplatePreSearchTask implements TreeSearchTask { + + protected TreeSearchCallback callback; + + //最外层的目录节点 + protected FileNode fileNode; + + public TemplatePreSearchTask(TreeSearchCallback callback, FileNode fileNode) { + this.callback = callback; + this.fileNode = fileNode; + } + + @Override + public void run() { + TreeSearchResult result; + try { + List allChildNode = new ArrayList<>(); + dealWithChildTemplate(allChildNode, fileNode); + result = new TemplateSearchResult.Builder().buildAddToNotCalculated(allChildNode).buildSuccess(true).build(); + FineLoggerFactory.getLogger().info("[Template Search] calculate {} child nodes success. total child node num is: {}", fileNode.getEnvPath(), allChildNode.size()); + } catch (Exception e) { + FineLoggerFactory.getLogger().error("[Template Search] calculate {} child nodes failed", fileNode.getEnvPath()); + result = new TemplateSearchResult.Builder().buildSuccess(false).build(); + } + callback.done(result); + } + + /** + * 加载最外层目录下所有的子节点 + * + * @param fileNodes + * @param fileNode + */ + protected void dealWithChildTemplate(List fileNodes, FileNode fileNode) { + FileNode[] childNodes = TemplateTreePane.getInstance().getTemplateFileTree().listFile(fileNode.getEnvPath()); + for (FileNode childNode : childNodes) { + if (childNode.isDirectory()) { + fileNodes.add(childNode); + dealWithChildTemplate(fileNodes, childNode); + } else { + fileNodes.add(childNode); + } + } + } +} diff --git a/designer-base/src/main/java/com/fr/design/search/TreeSearchManager.java b/designer-base/src/main/java/com/fr/design/search/TreeSearchManager.java new file mode 100644 index 000000000..2b527dca5 --- /dev/null +++ b/designer-base/src/main/java/com/fr/design/search/TreeSearchManager.java @@ -0,0 +1,19 @@ +package com.fr.design.search; + +import com.fr.design.search.event.TreeSearchStatusChangeListener; + +public interface TreeSearchManager { + + /** + * 注册搜索状态监听器 + */ + void registerTreeSearchStatusChangeListener(TreeSearchStatusChangeListener listener); + + + + /** + * 是否搜出结果 + * @return + */ + boolean isMatchSetsEmpty(); +} diff --git a/designer-base/src/main/java/com/fr/design/data/datapane/management/search/searcher/TreeSearchStatus.java b/designer-base/src/main/java/com/fr/design/search/TreeSearchStatus.java similarity index 84% rename from designer-base/src/main/java/com/fr/design/data/datapane/management/search/searcher/TreeSearchStatus.java rename to designer-base/src/main/java/com/fr/design/search/TreeSearchStatus.java index ff577995f..3b97a7d40 100644 --- a/designer-base/src/main/java/com/fr/design/data/datapane/management/search/searcher/TreeSearchStatus.java +++ b/designer-base/src/main/java/com/fr/design/search/TreeSearchStatus.java @@ -1,4 +1,4 @@ -package com.fr.design.data.datapane.management.search.searcher; +package com.fr.design.search; /** * @author Yvan diff --git a/designer-base/src/main/java/com/fr/design/data/datapane/management/search/searcher/TreeSearcher.java b/designer-base/src/main/java/com/fr/design/search/TreeSearcher.java similarity index 83% rename from designer-base/src/main/java/com/fr/design/data/datapane/management/search/searcher/TreeSearcher.java rename to designer-base/src/main/java/com/fr/design/search/TreeSearcher.java index b6f694470..bbc44e1ad 100644 --- a/designer-base/src/main/java/com/fr/design/data/datapane/management/search/searcher/TreeSearcher.java +++ b/designer-base/src/main/java/com/fr/design/search/TreeSearcher.java @@ -1,4 +1,4 @@ -package com.fr.design.data.datapane.management.search.searcher; +package com.fr.design.search; /** diff --git a/designer-base/src/main/java/com/fr/design/data/datapane/management/search/control/TreeSearchCallback.java b/designer-base/src/main/java/com/fr/design/search/control/TreeSearchCallback.java similarity index 69% rename from designer-base/src/main/java/com/fr/design/data/datapane/management/search/control/TreeSearchCallback.java rename to designer-base/src/main/java/com/fr/design/search/control/TreeSearchCallback.java index a3c8c5c98..3989902de 100644 --- a/designer-base/src/main/java/com/fr/design/data/datapane/management/search/control/TreeSearchCallback.java +++ b/designer-base/src/main/java/com/fr/design/search/control/TreeSearchCallback.java @@ -1,4 +1,4 @@ -package com.fr.design.data.datapane.management.search.control; +package com.fr.design.search.control; /** diff --git a/designer-base/src/main/java/com/fr/design/data/datapane/management/search/control/TreeSearchResult.java b/designer-base/src/main/java/com/fr/design/search/control/TreeSearchResult.java similarity index 57% rename from designer-base/src/main/java/com/fr/design/data/datapane/management/search/control/TreeSearchResult.java rename to designer-base/src/main/java/com/fr/design/search/control/TreeSearchResult.java index 320eca947..69225de49 100644 --- a/designer-base/src/main/java/com/fr/design/data/datapane/management/search/control/TreeSearchResult.java +++ b/designer-base/src/main/java/com/fr/design/search/control/TreeSearchResult.java @@ -1,4 +1,4 @@ -package com.fr.design.data.datapane.management.search.control; +package com.fr.design.search.control; import java.util.List; @@ -15,23 +15,24 @@ public interface TreeSearchResult { boolean isSuccess(); /** - * 数据集名称匹配或者列名匹配时,需要将数据集名称添加到匹配结果集中 + * 数据匹配时,需要将名称添加到匹配结果集中 * * @return */ List getAddToMatch(); /** - * 数据集有列名匹配时,需要添加到展开结果集中 + * 数据匹配时,需要添加到展开结果集中 * * @return */ List getAddToExpand(); /** - * 数据集完成计算后,需要添加到完成结果集中 + * 数据完成计算后,需要添加到完成结果集中 * * @return */ List getAddToCalculated(); + } diff --git a/designer-base/src/main/java/com/fr/design/data/datapane/management/search/control/TreeSearchTask.java b/designer-base/src/main/java/com/fr/design/search/control/TreeSearchTask.java similarity index 63% rename from designer-base/src/main/java/com/fr/design/data/datapane/management/search/control/TreeSearchTask.java rename to designer-base/src/main/java/com/fr/design/search/control/TreeSearchTask.java index 695c7d3c2..e7e4407ae 100644 --- a/designer-base/src/main/java/com/fr/design/data/datapane/management/search/control/TreeSearchTask.java +++ b/designer-base/src/main/java/com/fr/design/search/control/TreeSearchTask.java @@ -1,4 +1,4 @@ -package com.fr.design.data.datapane.management.search.control; +package com.fr.design.search.control; /** * @author Yvan diff --git a/designer-base/src/main/java/com/fr/design/data/datapane/management/search/event/TreeSearchStatusChangeEvent.java b/designer-base/src/main/java/com/fr/design/search/event/TreeSearchStatusChangeEvent.java similarity index 72% rename from designer-base/src/main/java/com/fr/design/data/datapane/management/search/event/TreeSearchStatusChangeEvent.java rename to designer-base/src/main/java/com/fr/design/search/event/TreeSearchStatusChangeEvent.java index 9d28e2d73..34d21a018 100644 --- a/designer-base/src/main/java/com/fr/design/data/datapane/management/search/event/TreeSearchStatusChangeEvent.java +++ b/designer-base/src/main/java/com/fr/design/search/event/TreeSearchStatusChangeEvent.java @@ -1,6 +1,6 @@ -package com.fr.design.data.datapane.management.search.event; +package com.fr.design.search.event; -import com.fr.design.data.datapane.management.search.searcher.TreeSearchStatus; +import com.fr.design.search.TreeSearchStatus; import java.util.EventObject; diff --git a/designer-base/src/main/java/com/fr/design/data/datapane/management/search/event/TreeSearchStatusChangeListener.java b/designer-base/src/main/java/com/fr/design/search/event/TreeSearchStatusChangeListener.java similarity index 76% rename from designer-base/src/main/java/com/fr/design/data/datapane/management/search/event/TreeSearchStatusChangeListener.java rename to designer-base/src/main/java/com/fr/design/search/event/TreeSearchStatusChangeListener.java index 63dc4e698..a5b1b8849 100644 --- a/designer-base/src/main/java/com/fr/design/data/datapane/management/search/event/TreeSearchStatusChangeListener.java +++ b/designer-base/src/main/java/com/fr/design/search/event/TreeSearchStatusChangeListener.java @@ -1,4 +1,4 @@ -package com.fr.design.data.datapane.management.search.event; +package com.fr.design.search.event; import java.util.EventListener; diff --git a/designer-base/src/main/java/com/fr/design/data/datapane/management/search/view/TreeSearchRendererHelper.java b/designer-base/src/main/java/com/fr/design/search/view/TreeSearchRendererHelper.java similarity index 81% rename from designer-base/src/main/java/com/fr/design/data/datapane/management/search/view/TreeSearchRendererHelper.java rename to designer-base/src/main/java/com/fr/design/search/view/TreeSearchRendererHelper.java index 340c6912a..0d2fd752c 100644 --- a/designer-base/src/main/java/com/fr/design/data/datapane/management/search/view/TreeSearchRendererHelper.java +++ b/designer-base/src/main/java/com/fr/design/search/view/TreeSearchRendererHelper.java @@ -1,6 +1,6 @@ -package com.fr.design.data.datapane.management.search.view; +package com.fr.design.search.view; -import com.fr.design.data.datapane.TableDataTree; +import com.fr.design.gui.itree.refreshabletree.RefreshableJTree; import javax.swing.JTree; import javax.swing.tree.DefaultTreeCellRenderer; @@ -29,19 +29,19 @@ public class TreeSearchRendererHelper { this.originTreeCellRenderer = originTreeCellRenderer; } - public void replaceTreeRenderer(TableDataTree tableDataTree, String searchText) { - tableDataTree.setCellRenderer(getNewTreeCellRenderer(searchText)); + public void replaceTreeRenderer(RefreshableJTree tree, String searchText) { + tree.setCellRenderer(getNewTreeCellRenderer(searchText)); } - public void save(TableDataTree tableDataTree) { + public void save(TreeCellRenderer originTreeCellRenderer) { if (getOriginTreeCellRenderer() == null) { - setOriginTreeCellRenderer(tableDataTree.getTableDataTreeCellRenderer()); + setOriginTreeCellRenderer(originTreeCellRenderer); } } - public void restore(TableDataTree tableDataTree) { + public void restore(RefreshableJTree tree) { if (getOriginTreeCellRenderer() != null) { - tableDataTree.setCellRenderer(getOriginTreeCellRenderer()); + tree.setCellRenderer(getOriginTreeCellRenderer()); } } diff --git a/designer-base/src/main/java/com/fr/file/FILEChooserPane.java b/designer-base/src/main/java/com/fr/file/FILEChooserPane.java index 6dfb29443..0561319db 100644 --- a/designer-base/src/main/java/com/fr/file/FILEChooserPane.java +++ b/designer-base/src/main/java/com/fr/file/FILEChooserPane.java @@ -27,6 +27,7 @@ import com.fr.design.layout.FRGUIPaneFactory; import com.fr.design.layout.TableLayout; import com.fr.design.layout.TableLayoutHelper; import com.fr.design.mainframe.DesignerFrame; +import com.fr.design.mainframe.DesignerFrameFileDealerPane; import com.fr.design.mainframe.JTemplate; import com.fr.design.utils.gui.GUICoreUtils; import com.fr.event.Event; @@ -104,6 +105,9 @@ import java.util.Set; import java.util.regex.Matcher; import java.util.regex.Pattern; +import static javax.swing.JOptionPane.DEFAULT_OPTION; +import static javax.swing.JOptionPane.WARNING_MESSAGE; + /* * FileChooserPane要高亮显示某Button,以显示当前路径 * 边距要调整 @@ -887,7 +891,7 @@ public class FILEChooserPane extends BasicPane { private void doOK() { // 如果没有写文件名,不允许打开 or save - if (fileNameTextField.getText().length() == 0) { + if (fileNameTextField.getText().length() == 0 || !validInput()) { return; } if (type == JFileChooser.OPEN_DIALOG) { @@ -918,6 +922,19 @@ public class FILEChooserPane extends BasicPane { } } + private boolean validInput() { + String name = fileNameTextField.getText().trim(); + if (!Pattern.compile(DesignerFrameFileDealerPane.FILE_NAME_LIMIT).matcher(name).matches()) { + FineJOptionPane.showConfirmDialog(this, + Toolkit.i18nText("Fine-Design_Basic_Template_Name_Illegal"), + Toolkit.i18nText("Fine-Design_Basic_Error"), + DEFAULT_OPTION, + WARNING_MESSAGE); + return false; + } + return true; + } + private void saveDialog() { String filename = fileNameTextField.getText(); diff --git a/designer-base/src/main/resources/com/fr/design/images/FileDealerPaneIcon/collapse-all.png b/designer-base/src/main/resources/com/fr/design/images/FileDealerPaneIcon/collapse-all.png new file mode 100644 index 0000000000000000000000000000000000000000..935503e65e0a86910c6d489c4365431f91f8cfd6 GIT binary patch literal 177 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`6`n4RAr}5yCpq#pDDbef&wqLP z%JhflO=fpwdMs?$5@*`ty!*CO(bN<>gTe~DWM4fcbY*i literal 0 HcmV?d00001 diff --git a/designer-base/src/main/resources/com/fr/design/images/FileDealerPaneIcon/locate.png b/designer-base/src/main/resources/com/fr/design/images/FileDealerPaneIcon/locate.png new file mode 100644 index 0000000000000000000000000000000000000000..0773525e3dff31d230901f76f2fa97ccddd5a7fe GIT binary patch literal 335 zcmV-V0kHmwP)5C-7yZNLUBfeMX^mk2gML!nmaRjBj|^($!D03ty}fr=&AfECCjGjiU|yCF<+ zbMHN8&OiS@|D1t;#^8tZcey|8%j*L;#1iK7;0qoQ>titq;0-f4$8Bb>U<>bcCI1!+B@1g(z002ovPDHLkV1krgkOlw% literal 0 HcmV?d00001 diff --git a/designer-base/src/main/resources/com/fr/design/images/m_edit/move.png b/designer-base/src/main/resources/com/fr/design/images/m_edit/move.png new file mode 100644 index 0000000000000000000000000000000000000000..9d924c78d435875e3c0811279d9c5e6b096d484e GIT binary patch literal 328 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbK}a)3{WE0Bf&ZEbBcGc!X&b0aV? zG&D6b0wH5#AZcO@1|}vz22cb@0~sI$mV%OSE<_4K{{R2qN@~F~pdAV&L4Lsuj7)55 zS{Al;L1Fm?#pMkhbLOpHzj4d%eMgRd{QUFR?|-F&(|~Fjlf2zs6gFquHUK&4o-U3d z5|@(`4lwT65%5#zlF^zIHEw~Lnu2N~s(=1uq$zN3;5f+A65&wemsx0MXIE&Tc$QP- z!PMyj0s<4Jum}l#+M}QgTe~DWM4f6ANGD literal 0 HcmV?d00001