diff --git a/designer-base/src/main/java/com/fr/design/DesignerEnvManager.java b/designer-base/src/main/java/com/fr/design/DesignerEnvManager.java index cec7b921d..f7bff1f5b 100644 --- a/designer-base/src/main/java/com/fr/design/DesignerEnvManager.java +++ b/designer-base/src/main/java/com/fr/design/DesignerEnvManager.java @@ -6,6 +6,7 @@ package com.fr.design; import com.fr.base.BaseXMLUtils; import com.fr.base.Utils; import com.fr.design.actions.help.alphafine.AlphaFineConfigManager; +import com.fr.design.carton.SwitchForSwingChecker; import com.fr.design.constants.UIConstants; import com.fr.design.data.DesignTableDataManager; import com.fr.design.dialog.ErrorDialog; @@ -192,6 +193,8 @@ public class DesignerEnvManager implements XMLReadable, XMLWriter { private VcsConfigManager vcsConfigManager = VcsConfigManager.getInstance(); private DesignerStartupConfig designerStartupConfig = DesignerStartupConfig.getInstance(); + + private SwitchForSwingChecker switchForSwingChecker = SwitchForSwingChecker.getInstance(); public static final String CAS_CERTIFICATE_PATH = "certificatePath"; @@ -1872,6 +1875,8 @@ public class DesignerEnvManager implements XMLReadable, XMLWriter { readDesignerLoginAttr(reader); } else if (name.equals(fvsDesignerConfig.getName())) { readFvsDesignerConfig(reader); + } else if (name.equals(SwitchForSwingChecker.XML_TAG)) { + readSwitchForSwingCheckerAttr(reader); } else { readLayout(reader, name); } @@ -2091,6 +2096,10 @@ public class DesignerEnvManager implements XMLReadable, XMLWriter { reader.readXMLObject(DesignerPort.getInstance()); } + private void readSwitchForSwingCheckerAttr(XMLableReader reader) { + reader.readXMLObject(switchForSwingChecker); + } + /** * Write XML.
* The method will be invoked when save data to XML file.
@@ -2123,6 +2132,7 @@ public class DesignerEnvManager implements XMLReadable, XMLWriter { writeComponentReuseNotificationInfo(writer); writeDesignerLoginAttr(writer); writeFvsDesignerConfig(writer); + writeSwitchForSwingChecker(writer); writer.end(); } @@ -2437,6 +2447,10 @@ public class DesignerEnvManager implements XMLReadable, XMLWriter { this.fvsDesignerConfig.writeXML(writer); } + private void writeSwitchForSwingChecker(XMLPrintWriter writer) { + this.switchForSwingChecker.writeXML(writer); + } + enum XmlHandler { Self; diff --git a/designer-base/src/main/java/com/fr/design/EnvChangeEntrance.java b/designer-base/src/main/java/com/fr/design/EnvChangeEntrance.java index 661d5f05e..d38def283 100644 --- a/designer-base/src/main/java/com/fr/design/EnvChangeEntrance.java +++ b/designer-base/src/main/java/com/fr/design/EnvChangeEntrance.java @@ -1,6 +1,7 @@ package com.fr.design; import com.fr.common.report.ReportState; +import com.fr.design.mainframe.manager.clip.TemplateTreeClipboard; import com.fr.design.plugin.remind.PluginErrorDesignReminder; import com.fr.design.data.DesignTableDataManager; import com.fr.design.dialog.BasicDialog; @@ -150,6 +151,8 @@ public class EnvChangeEntrance { model.envChanged(); } NotificationCenter.getInstance().clearAllNotifications(); + //切换环境后,清空粘贴板里面的内容 + TemplateTreeClipboard.getInstance().reset(); return true; } 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..896c51878 --- /dev/null +++ b/designer-base/src/main/java/com/fr/design/actions/file/DelFileAction.java @@ -0,0 +1,50 @@ +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 com.fr.design.utils.TemplateUtils; + +import javax.swing.JOptionPane; +import java.awt.event.ActionEvent; + +import static javax.swing.JOptionPane.WARNING_MESSAGE; +import static javax.swing.JOptionPane.YES_NO_OPTION; + +/* + * 删除指定文件 + */ +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; + } + if (TemplateUtils.checkSelectedTemplateIsEditing()) { + if (FineJOptionPane.showConfirmDialog(DesignerContext.getDesignerFrame(), + Toolkit.i18nText("Fine-Design_Basic_Template_Is_Editing"), + Toolkit.i18nText("Fine-Design_Basic_Alert"), + YES_NO_OPTION) != JOptionPane.YES_OPTION) { + 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..db308181d --- /dev/null +++ b/designer-base/src/main/java/com/fr/design/actions/file/LocateAction.java @@ -0,0 +1,159 @@ +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.filetree.TemplateFileTree; +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(), false); + } + + /** + * 在左侧模板树定位到指定模板 + * + * @param locatedPath + */ + public static void gotoEditingTemplateLeaf(String locatedPath) { + gotoEditingTemplateLeaf(locatedPath, true); + } + + private static void gotoEditingTemplateLeaf(String locatedPath, boolean needRefreshMode) { + if (locatedPath == null) { + return; + } + + DefaultTreeModel model = (DefaultTreeModel) getTemplateFileTree().getModel(); + ExpandMutableTreeNode treeNode = (ExpandMutableTreeNode) model.getRoot(); + if (needRefreshMode) { + treeNode.removeAllChildren(); + ExpandMutableTreeNode[] childTreeNodes = getTemplateFileTree().loadChildTreeNodes(treeNode); + treeNode.addChildTreeNodes(childTreeNodes); + model.reload(treeNode); + } + + recursiveSelectPath(treeNode, locatedPath, model); + TreePath selectedTreePath = getTemplateFileTree().getSelectionPath(); + if (selectedTreePath != null) { + getTemplateFileTree().scrollPathToVisible(selectedTreePath); + } + } + + private static TemplateFileTree getTemplateFileTree() { + return TemplateTreePane.getInstance().getTemplateFileTree(); + } + + 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))) { + 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 = 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/PreferencePane.java b/designer-base/src/main/java/com/fr/design/actions/file/PreferencePane.java index 51b0ed2a7..fdc0a46ae 100644 --- a/designer-base/src/main/java/com/fr/design/actions/file/PreferencePane.java +++ b/designer-base/src/main/java/com/fr/design/actions/file/PreferencePane.java @@ -241,9 +241,9 @@ public class PreferencePane extends BasicPane { createLogPane(advancePane); createLanPane(generalPane); + + createStartupPagePane(generalPane); - // 先屏蔽下 - // createStartupPagePane(generalPane); createLengthPane(advancePane); createServerPane(advancePane); @@ -655,13 +655,13 @@ public class PreferencePane extends BasicPane { // ben:选择版本语言; JPanel startupPagePaneWrapper = FRGUIPaneFactory.createX_AXISBoxInnerContainer_S_Pane(); - JPanel startupPane = FRGUIPaneFactory.createTopVerticalTitledBorderPane("启动页配置"); + JPanel startupPane = FRGUIPaneFactory.createTopVerticalTitledBorderPane(Toolkit.i18nText("Fine-Design_Startup_Page_Config")); generalPane.add(startupPagePaneWrapper); startupPagePaneWrapper.add(startupPane); - startupPageEnabledCheckBox = new UICheckBox("启动设计器时,自动打开启动页"); + startupPageEnabledCheckBox = new UICheckBox(Toolkit.i18nText("Fine-Design_Startup_Page_Config_Check_Text")); startupPane.add(startupPageEnabledCheckBox); - UILabel descLabel = new UILabel("注意:若在远程环境下直接关闭,再次启动时,启动速度会变慢"); + UILabel descLabel = new UILabel(Toolkit.i18nText("Fine-Design_Startup_Page_Config_Desc")); descLabel.setForeground(new Color(51, 51, 52, (int)Math.round(0.5 * 255))); startupPane.add(descLabel); } @@ -854,7 +854,7 @@ public class PreferencePane extends BasicPane { previewResolutionBtnM.setEnabled(enabled); this.cloudAnalyticsDelayCheckBox.setSelected(designerEnvManager.isCloudAnalyticsDelay()); -// this.startupPageEnabledCheckBox.setSelected(designerEnvManager.isStartupPageEnabled()); + this.startupPageEnabledCheckBox.setSelected(designerEnvManager.isStartupPageEnabled()); } private int chooseCase(int sign) { @@ -921,7 +921,7 @@ public class PreferencePane extends BasicPane { vcsConfigManager.setVcsEnable(this.vcsEnableCheckBox.isSelected()); vcsConfigManager.setSaveCommit(this.saveCommitCheckBox.isSelected()); vcsConfigManager.setUseInterval(this.useIntervalCheckBox.isSelected()); -// designerEnvManager.setStartupPageEnabled(this.startupPageEnabledCheckBox.isSelected()); + designerEnvManager.setStartupPageEnabled(this.startupPageEnabledCheckBox.isSelected()); Configurations.update(new Worker() { @Override public void run() { 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..efa6d1216 --- /dev/null +++ b/designer-base/src/main/java/com/fr/design/actions/file/RenameAction.java @@ -0,0 +1,343 @@ +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.mainframe.manager.search.TemplateTreeSearchManager; +import com.fr.design.utils.TemplateUtils; +import com.fr.design.utils.gui.GUICoreUtils; +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.JOptionPane; +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; +import static javax.swing.JOptionPane.YES_NO_OPTION; + +/** + * 模板/目录重命名操作 + */ +public class RenameAction extends UpdateAction { + + private FileOperations selectedOperation; + + 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(false); + 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() { + + if (TemplateUtils.checkSelectedTemplateIsEditing()) { + if (FineJOptionPane.showConfirmDialog(this, + Toolkit.i18nText("Fine-Design_Basic_Template_Is_Editing"), + Toolkit.i18nText("Fine-Design_Basic_Alert"), + YES_NO_OPTION) != JOptionPane.YES_OPTION) { + return; + } + } + + 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) { + afterRename(path, newPath); + } else { + FineJOptionPane.showConfirmDialog(DesignerContext.getDesignerFrame(), + Toolkit.i18nText("Fine-Design_Basic_Rename_Failure"), + Toolkit.i18nText("Fine-Design_Basic_Error"), + DEFAULT_OPTION, + ERROR_MESSAGE); + } + } + + private void afterRename(String path, String newPath) { + EventDispatcher.fire(DesignerFrameFileDealerPane.TEMPLATE_RENAME, new TwoTuple<>(path, newPath)); + HistoryTemplateListCache.getInstance().rename(fnf, path, newPath); + DesignerEnvManager.getEnvManager().replaceRecentOpenedFilePath(fnf.isDirectory(), path, newPath); + selectedOperation.refreshParent(); + DesignerContext.getDesignerFrame().setTitle(); + if (TemplateTreeSearchManager.getInstance().isInSearchMode()) { + TemplateTreeSearchManager.getInstance().outOfSearchMode(); + LocateAction.gotoEditingTemplateLeaf(newPath); + } + } + + + 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/actions/help/alphafine/AlphaFineConfigManager.java b/designer-base/src/main/java/com/fr/design/actions/help/alphafine/AlphaFineConfigManager.java index 3d36e563c..b7df52c30 100644 --- a/designer-base/src/main/java/com/fr/design/actions/help/alphafine/AlphaFineConfigManager.java +++ b/designer-base/src/main/java/com/fr/design/actions/help/alphafine/AlphaFineConfigManager.java @@ -10,18 +10,16 @@ import com.fr.stable.xml.XMLPrintWriter; import com.fr.stable.xml.XMLReadable; import com.fr.stable.xml.XMLable; import com.fr.stable.xml.XMLableReader; -import java.util.Arrays; -import java.util.HashSet; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Set; -import java.util.Stack; import org.jetbrains.annotations.NotNull; -import javax.swing.*; +import javax.swing.KeyStroke; import java.util.Collections; import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedHashMap; import java.util.Map; +import java.util.Set; +import java.util.Stack; /** * AlphaFine配置类 @@ -54,13 +52,17 @@ public class AlphaFineConfigManager implements XMLable { */ private boolean containRecommend = true; /** - * 设置 + * 功能 */ private boolean containAction = true; /** * 帮助文档 */ private boolean containDocument = true; + /** + * 我的模板 + * */ + private boolean containMyTemplate = true; /** * 模板 */ @@ -70,7 +72,7 @@ public class AlphaFineConfigManager implements XMLable { */ private boolean containFileContent; /** - * 应用中心 + * 插件中心 */ private boolean containPlugin = true; /** @@ -95,6 +97,11 @@ public class AlphaFineConfigManager implements XMLable { */ private boolean productDynamics = true; + /** + * 模板商城是否展示 + * */ + private boolean showTemplateShop = true; + private Map actionSearchTextCache = new HashMap<>(8); private String cacheBuildNO; @@ -326,6 +333,14 @@ public class AlphaFineConfigManager implements XMLable { this.containDocument = containDocument; } + public void setContainMyTemplate(boolean containMyTemplate) { + this.containMyTemplate = containMyTemplate; + } + + public boolean isContainMyTemplate() { + return containMyTemplate; + } + public boolean isContainTemplate() { return containTemplate; } @@ -446,6 +461,14 @@ public class AlphaFineConfigManager implements XMLable { return productDynamics && FRContext.isChineseEnv(); } + public boolean hasTemplateShop() { + return showTemplateShop && FRContext.isChineseEnv(); + } + + public void setShowTemplateShop(boolean showTemplateShop) { + this.showTemplateShop = showTemplateShop; + } + public void setProductDynamics(boolean productDynamics) { this.productDynamics = productDynamics; } diff --git a/designer-base/src/main/java/com/fr/design/actions/help/alphafine/AlphaFineConfigPane.java b/designer-base/src/main/java/com/fr/design/actions/help/alphafine/AlphaFineConfigPane.java index 55d467e88..fa0f4a8f0 100644 --- a/designer-base/src/main/java/com/fr/design/actions/help/alphafine/AlphaFineConfigPane.java +++ b/designer-base/src/main/java/com/fr/design/actions/help/alphafine/AlphaFineConfigPane.java @@ -1,17 +1,29 @@ package com.fr.design.actions.help.alphafine; import com.fr.base.FRContext; +import com.fr.base.svg.IconUtils; import com.fr.design.DesignerEnvManager; import com.fr.design.dialog.BasicPane; import com.fr.design.gui.icheckbox.UICheckBox; import com.fr.design.gui.ilable.UILabel; +import com.fr.design.gui.imenu.UIPopupMenu; import com.fr.design.gui.itextfield.UITextField; +import com.fr.design.i18n.Toolkit; import com.fr.design.layout.FRGUIPaneFactory; import com.fr.design.layout.TableLayoutHelper; +import com.fr.design.utils.gui.GUICoreUtils; import com.fr.log.FineLoggerFactory; -import javax.swing.*; -import java.awt.*; +import javax.swing.BorderFactory; +import javax.swing.JButton; +import javax.swing.JPanel; +import javax.swing.KeyStroke; +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.Component; +import java.awt.Dimension; +import java.awt.FlowLayout; +import java.awt.Insets; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.KeyAdapter; @@ -25,14 +37,22 @@ import java.awt.event.MouseEvent; public class AlphaFineConfigPane extends BasicPane { private static final String TYPE = "pressed"; private static final String DISPLAY_TYPE = "+"; + private static final Color LABEL_TEXT = new Color(0x919193); - - private static final double COLUMN_GAP = 180; - private static final double ROW_GAP = 25; + private static final double COLUMN_WIDTH = 150; + private static final double ROW_HEIGHT = 25; private KeyStroke shortCutKeyStore = null; - private UICheckBox enabledCheckbox, searchOnlineCheckbox, needSegmentationCheckbox, needIntelligentCustomerService, productDynamicsCheckbox, containActionCheckbox, containDocumentCheckbox, containTemplateCheckbox, containPluginCheckbox, containFileContentCheckbox; + private UICheckBox enabledCheckbox, searchOnlineCheckbox, needSegmentationCheckbox; + private UICheckBox productDynamicsCheckbox, containTemplateShopCheckbox, containDocumentCheckbox, + containPluginCheckbox, containActionCheckbox, containMyTemplateCheckbox; private UITextField shortcutsField; + // 搜索范围-我的模板,相关组件 + private JPanel containMyTemplatePane; + private JButton myTemplateSearchConfigButton; + private UIPopupMenu myTemplateSearchMenu; + private UICheckBox containTemplateNameSearchCheckbox, containFileContentSearchCheckbox; + public AlphaFineConfigPane() { this.initComponents(); } @@ -45,15 +65,6 @@ public class AlphaFineConfigPane extends BasicPane { createSearchConfigPane(contentPane); this.setLayout(FRGUIPaneFactory.createBorderLayout()); this.add(contentPane, BorderLayout.NORTH); - - } - - private Component[][] initSearchRangeComponents() { - Component[][] components = new Component[][]{ - new Component[]{productDynamicsCheckbox, containActionCheckbox, containDocumentCheckbox}, - new Component[]{containTemplateCheckbox, containPluginCheckbox, containFileContentCheckbox}, - }; - return components; } private Component[][] initOnlineComponents() { @@ -63,24 +74,99 @@ public class AlphaFineConfigPane extends BasicPane { return components; } + // 搜索范围 private void createSearchConfigPane(JPanel contentPane) { - double[] rowSize = {ROW_GAP, ROW_GAP, ROW_GAP}; - - double[] columnSize = {COLUMN_GAP, COLUMN_GAP, COLUMN_GAP}; + double[] rowSize = {ROW_HEIGHT, ROW_HEIGHT, ROW_HEIGHT}; + double[] columnSize = {COLUMN_WIDTH, COLUMN_WIDTH, COLUMN_WIDTH}; JPanel northPane = FRGUIPaneFactory.createTitledBorderPane(com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Basic_AlphaFine_Search_Range")); - productDynamicsCheckbox = new UICheckBox(com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Report_AlphaFine_Product_Dynamics")); - containActionCheckbox = new UICheckBox(com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Report_Set")); + productDynamicsCheckbox = new UICheckBox(com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Report_AlphaFine_Product_News")); + containActionCheckbox = new UICheckBox(com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Report_Function")); containPluginCheckbox = new UICheckBox(com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Report_Plugin_Addon")); containDocumentCheckbox = new UICheckBox(com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Report_Community_Help")); - containTemplateCheckbox = new UICheckBox(com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Report_Templates")); - containFileContentCheckbox = new UICheckBox(com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Basic_Templates_Content")); - needIntelligentCustomerService = new UICheckBox(com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Basic_AlphaFine_Intelligent_Customer_Service")); + containMyTemplateCheckbox = new UICheckBox(com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Report_My_Templates")); + containFileContentSearchCheckbox = new UICheckBox(com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Basic_Templates_Content")); + containTemplateShopCheckbox = new UICheckBox(Toolkit.i18nText("Fine-Design_Report_AlphaFine_Template_Shop")); + containMyTemplateCheckbox = new UICheckBox(Toolkit.i18nText("Fine-Design_Report_My_Templates")); JPanel searchConfigPane = TableLayoutHelper.createTableLayoutPane(initSearchRangeComponents(), rowSize, columnSize); northPane.add(searchConfigPane); contentPane.add(northPane); } + private Component[][] initSearchRangeComponents() { + // 我的模板checkbox设置,点击后 + initMyTemplateSearchPane(); + + Component[][] components = new Component[][]{ + new Component[]{productDynamicsCheckbox, containTemplateShopCheckbox, containDocumentCheckbox}, + new Component[]{containPluginCheckbox, containActionCheckbox, containMyTemplatePane}, + }; + + for (int i = 0; i < components.length; i++) { + for (int j = 0; j < components[i].length; j++) { + if (components[i][j] instanceof UICheckBox) { + UICheckBox box = (UICheckBox) components[i][j]; + } + } + } + return components; + } + + /** + * 【搜索范围】中的复选框有无选中的 + * */ + private boolean hasSelectedSearchRangeCheckBox() { + return productDynamicsCheckbox.isSelected() || containTemplateShopCheckbox.isSelected() || containDocumentCheckbox.isSelected() + || containPluginCheckbox.isSelected() || containActionCheckbox.isSelected() || containMyTemplateCheckbox.isSelected(); + } + + // 搜索范围-我的模板 + private void initMyTemplateSearchPane() { + containMyTemplatePane = new JPanel(new FlowLayout(FlowLayout.LEFT,4,5)); + containMyTemplateCheckbox.setBorder(BorderFactory.createEmptyBorder()); + containMyTemplateCheckbox.addActionListener( + new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + if (containMyTemplateCheckbox.isSelected()) { + myTemplateSearchConfigButton.setVisible(true); + } else { + myTemplateSearchConfigButton.setVisible(false); + } + } + } + ); + myTemplateSearchConfigButton = new JButton(); + myTemplateSearchConfigButton.setBorder(BorderFactory.createEmptyBorder()); + myTemplateSearchConfigButton.setMargin(new Insets(0,0,0,0)); + myTemplateSearchConfigButton.setIcon(IconUtils.readIcon("/com/fr/design/mainframe/alphafine/images/config.svg")); + myTemplateSearchMenu = new UIPopupMenu(); + containTemplateNameSearchCheckbox = new UICheckBox(Toolkit.i18nText("Fine-Design_AlphaFine_Config_Name_Search")); + containFileContentSearchCheckbox = new UICheckBox(Toolkit.i18nText("Fine-Design_AlphaFine_Config_Content_Search")); + containTemplateNameSearchCheckbox.setSelected(true); + containTemplateNameSearchCheckbox.setEnabled(false); + containTemplateNameSearchCheckbox.setBackground(null); + containFileContentSearchCheckbox.setBackground(null); + myTemplateSearchMenu.add(containTemplateNameSearchCheckbox); + myTemplateSearchMenu.add(containFileContentSearchCheckbox); + myTemplateSearchConfigButton.addMouseListener(new MouseAdapter() { + @Override + public void mouseClicked(MouseEvent e) { + super.mouseClicked(e); + GUICoreUtils.showPopupMenu(myTemplateSearchMenu, containMyTemplatePane, containMyTemplateCheckbox.getWidth(), containMyTemplatePane.getY()); + } + + @Override + public void mouseMoved(MouseEvent e) { + super.mouseMoved(e); + myTemplateSearchMenu.setVisible(false); + } + }); + containMyTemplatePane.add("containMyTemplateCheckbox", containMyTemplateCheckbox); + containMyTemplatePane.add("myTemplateSearchConfigButton", myTemplateSearchConfigButton); + } + + private void createShortcutsPane(JPanel contentPane) { JPanel northPane = FRGUIPaneFactory.createTitledBorderPane(com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Basic_AlphaFine_Shortcut_Config")); shortcutsField = new UITextField(); @@ -91,7 +177,7 @@ public class AlphaFineConfigPane extends BasicPane { northPane.add(new UILabel(com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Basic_Open") + ":")); northPane.add(shortcutsField); UILabel label = new UILabel(com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Basic_AlphaFine_SetShortcuts")); - label.setForeground(Color.RED); + label.setForeground(LABEL_TEXT); northPane.add(label); contentPane.add(northPane); } @@ -130,21 +216,21 @@ public class AlphaFineConfigPane extends BasicPane { productDynamicsCheckbox.setEnabled(false); containPluginCheckbox.setEnabled(false); containDocumentCheckbox.setEnabled(false); - needIntelligentCustomerService.setEnabled(false); + containTemplateShopCheckbox.setEnabled(false); productDynamicsCheckbox.setSelected(false); containPluginCheckbox.setSelected(false); containDocumentCheckbox.setSelected(false); - needIntelligentCustomerService.setSelected(false); + containTemplateShopCheckbox.setSelected(false); } else { productDynamicsCheckbox.setEnabled(true); containPluginCheckbox.setEnabled(true); containDocumentCheckbox.setEnabled(true); - needIntelligentCustomerService.setEnabled(true); + containTemplateShopCheckbox.setEnabled(true); } } }); - double[] rowSize = {ROW_GAP}; - double[] columnSize = {COLUMN_GAP, COLUMN_GAP, COLUMN_GAP}; + double[] rowSize = {ROW_HEIGHT}; + double[] columnSize = {COLUMN_WIDTH, COLUMN_WIDTH, COLUMN_WIDTH}; JPanel onlinePane = TableLayoutHelper.createTableLayoutPane(initOnlineComponents(), rowSize, columnSize); northPane.add(onlinePane); contentPane.add(northPane); @@ -172,8 +258,10 @@ public class AlphaFineConfigPane extends BasicPane { this.searchOnlineCheckbox.setSelected(enabled4Locale); this.containActionCheckbox.setSelected(alphaFineConfigManager.isContainAction()); - this.containTemplateCheckbox.setSelected(alphaFineConfigManager.isContainTemplate()); - this.containFileContentCheckbox.setSelected(alphaFineConfigManager.isContainFileContent()); + + this.containMyTemplateCheckbox.setSelected(alphaFineConfigManager.isContainMyTemplate()); + this.myTemplateSearchConfigButton.setVisible(alphaFineConfigManager.isContainMyTemplate()); + this.containFileContentSearchCheckbox.setSelected(alphaFineConfigManager.isContainFileContent()); this.containDocumentCheckbox.setSelected(alphaFineConfigManager.isContainDocument() && enabled4Locale); this.containDocumentCheckbox.setEnabled(enabled4Locale); @@ -184,13 +272,13 @@ public class AlphaFineConfigPane extends BasicPane { this.productDynamicsCheckbox.setSelected(alphaFineConfigManager.isProductDynamics() && enabled4Locale); this.productDynamicsCheckbox.setEnabled(enabled4Locale); + this.containTemplateShopCheckbox.setSelected(alphaFineConfigManager.hasTemplateShop() && enabled4Locale); + this.containTemplateShopCheckbox.setEnabled(enabled4Locale); + this.shortcutsField.setText(AlphaFineShortCutUtil.getDisplayShortCut(alphaFineConfigManager.getShortcuts())); this.needSegmentationCheckbox.setSelected(alphaFineConfigManager.isNeedSegmentationCheckbox()); - this.needIntelligentCustomerService.setSelected(alphaFineConfigManager.isNeedIntelligentCustomerService() && enabled4Locale); - this.needIntelligentCustomerService.setEnabled(enabled4Locale); - shortCutKeyStore = convert2KeyStroke(alphaFineConfigManager.getShortcuts()); } @@ -201,12 +289,12 @@ public class AlphaFineConfigPane extends BasicPane { alphaFineConfigManager.setContainAction(this.containActionCheckbox.isSelected()); alphaFineConfigManager.setContainDocument(this.containDocumentCheckbox.isSelected()); alphaFineConfigManager.setProductDynamics(this.productDynamicsCheckbox.isSelected()); + alphaFineConfigManager.setShowTemplateShop(this.containTemplateShopCheckbox.isSelected()); alphaFineConfigManager.setEnabled(this.enabledCheckbox.isSelected()); alphaFineConfigManager.setSearchOnLine(this.searchOnlineCheckbox.isSelected()); - alphaFineConfigManager.setContainTemplate(this.containTemplateCheckbox.isSelected()); - alphaFineConfigManager.setContainFileContent(this.containFileContentCheckbox.isSelected()); + alphaFineConfigManager.setContainMyTemplate(this.containMyTemplateCheckbox.isSelected()); + alphaFineConfigManager.setContainFileContent(this.containFileContentSearchCheckbox.isSelected()); alphaFineConfigManager.setNeedSegmentationCheckbox(this.needSegmentationCheckbox.isSelected()); - alphaFineConfigManager.setNeedIntelligentCustomerService(this.needIntelligentCustomerService.isSelected()); alphaFineConfigManager.setShortcuts(shortCutKeyStore != null ? shortCutKeyStore.toString().replace(TYPE, DISPLAY_TYPE) : this.shortcutsField.getText()); designerEnvManager.setAlphaFineConfigManager(alphaFineConfigManager); try { @@ -233,10 +321,10 @@ public class AlphaFineConfigPane extends BasicPane { } public UICheckBox getIsContainFileContentCheckbox() { - return containFileContentCheckbox; + return containFileContentSearchCheckbox; } public void setIsContainFileContentCheckbox(UICheckBox isContainFileContentCheckbox) { - this.containFileContentCheckbox = isContainFileContentCheckbox; + this.containFileContentSearchCheckbox = isContainFileContentCheckbox; } } diff --git a/designer-base/src/main/java/com/fr/design/carton/CartonFiles.java b/designer-base/src/main/java/com/fr/design/carton/CartonFiles.java new file mode 100644 index 000000000..c2e39def9 --- /dev/null +++ b/designer-base/src/main/java/com/fr/design/carton/CartonFiles.java @@ -0,0 +1,28 @@ +package com.fr.design.carton; + +import java.io.File; + +public class CartonFiles { + private File easyCheckerFile; + private File timerCheckerFile; + + public CartonFiles() { + + } + + public File getEasyCheckerFile() { + return easyCheckerFile; + } + + public void setEasyCheckerFile(File easyCheckerFile) { + this.easyCheckerFile = easyCheckerFile; + } + + public File getTimerCheckerFile() { + return timerCheckerFile; + } + + public void setTimerCheckerFile(File timerCheckerFile) { + this.timerCheckerFile = timerCheckerFile; + } +} diff --git a/designer-base/src/main/java/com/fr/design/carton/CartonThreadExecutorPool.java b/designer-base/src/main/java/com/fr/design/carton/CartonThreadExecutorPool.java new file mode 100644 index 000000000..624bb2bb6 --- /dev/null +++ b/designer-base/src/main/java/com/fr/design/carton/CartonThreadExecutorPool.java @@ -0,0 +1,161 @@ +package com.fr.design.carton; + +import com.fr.base.SimpleDateFormatThreadSafe; +import com.fr.design.i18n.Toolkit; +import com.fr.json.JSONObject; + +import java.util.Map; +import java.util.Timer; +import java.util.TimerTask; +import java.util.concurrent.*; +import java.util.concurrent.atomic.AtomicLong; + + +public class CartonThreadExecutorPool extends ThreadPoolExecutor { + private static final int MAX_LIVE_TIME = 3000; + private static final int MAX_WORKER_THREADS = 10; + /** + * 开启间隔检测后两次检测的相隔时间ms + */ + private static final long CHECK_INTERVAL_MS = 100; + private final ThreadLocal startReportedStack = new ThreadLocal<>(); + private volatile static CartonThreadExecutorPool cartonThreadExecutorPool; + private static final ConcurrentHashMap concurrentHashMap = new ConcurrentHashMap<>(); + private static final SimpleDateFormatThreadSafe simpleDateFormatThreadSafe = new SimpleDateFormatThreadSafe(); + private final static AtomicLong hangCount = new AtomicLong(0); + private Timer timer; + /** + * 一个变量,用于控制easy监测模式的开关 + */ + private boolean easyWitch = false; + + public boolean isEasyWitch() { + return easyWitch; + } + + public void setEasyWitch(boolean easyWitch) { + this.easyWitch = easyWitch; + } + + private static class ThreadInfo { + private ThreadInfo () { + hangNumber = hangCount.getAndAdd(1); + } + private long hangNumber; + private final Thread eventThread = Thread.currentThread(); + private StackTraceElement[] lastReportedStack; + private final long startTime = System.currentTimeMillis(); + public void checkForHang() { + if (timeSoFar() > MAX_LIVE_TIME) { + examineHang(); + } + } + private long timeSoFar() { + return (System.currentTimeMillis() - startTime); + } + + private void examineHang() { + StackTraceElement[] currentStack = eventThread.getStackTrace(); + + if (lastReportedStack!=null && EventDispatchThreadHangMonitor.stacksEqual(currentStack, lastReportedStack)) { + return; + } + lastReportedStack = currentStack; + String stackTrace = EventDispatchThreadHangMonitor.stackTraceToString(currentStack); + JSONObject jsonObject = new JSONObject(); + jsonObject.put(Toolkit.i18nText("Fine-Design_Basic_Carton_Output_Time"), simpleDateFormatThreadSafe.format(System.currentTimeMillis())); + jsonObject.put(Toolkit.i18nText("Fine-Design_Basic_Carton_Event_Number"), "swingWorker_" + hangNumber); + jsonObject.put(Toolkit.i18nText("Fine-Design_Basic_Carton_Duration_Task_Execute"), timeSoFar()); + jsonObject.put(Toolkit.i18nText("Fine-Design_Basic_Carton_Stack_Info"), stackTrace); + EventDispatchThreadHangMonitor.outPutJournalLog(jsonObject.toString(), SwitchForSwingChecker.TIMER_CHECK_FLAG); + EventDispatchThreadHangMonitor.checkForDeadlock(); + } + } + + /** + * 来自SwingWorker类 + */ + public static ThreadFactory threadFactory = + new ThreadFactory() { + final ThreadFactory defaultFactory = + Executors.defaultThreadFactory(); + @Override + public Thread newThread(final Runnable r) { + Thread thread = + defaultFactory.newThread(r); + thread.setName("SwingWorker-" + + thread.getName()); + thread.setDaemon(true); + return thread; + } + }; + + public static CartonThreadExecutorPool getTimerThreadExecutorPool () { + if (cartonThreadExecutorPool == null) { + synchronized (CartonThreadExecutorPool.class) { + if (cartonThreadExecutorPool == null) { + cartonThreadExecutorPool = + new CartonThreadExecutorPool(MAX_WORKER_THREADS, MAX_WORKER_THREADS, + 10L, TimeUnit.MINUTES, + new LinkedBlockingQueue(),threadFactory + ); + } + } + } + return cartonThreadExecutorPool; + } + + private CartonThreadExecutorPool(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue workQueue, ThreadFactory threadFactory) { + super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory); + simpleDateFormatThreadSafe.applyPattern("yyyy-MM-dd HH:mm:ss"); + } + + @Override + protected void beforeExecute(Thread t, Runnable r) { + super.beforeExecute(t, r); + startReportedStack.set(t.getStackTrace()); + concurrentHashMap.put(t.getId(), new ThreadInfo()); + } + + @Override + protected void afterExecute(Runnable r, Throwable t) { + super.afterExecute(r, t); + long currentThreadId = Thread.currentThread().getId(); + long runTime = (System.currentTimeMillis() - concurrentHashMap.get(currentThreadId).startTime); + //加~是为了之后输出的时候换行。 + if (isEasyWitch() && runTime > MAX_LIVE_TIME) { + JSONObject jsonObject = new JSONObject(); + jsonObject.put(Toolkit.i18nText("Fine-Design_Basic_Carton_Output_Time"), simpleDateFormatThreadSafe.format(System.currentTimeMillis())); + jsonObject.put(Toolkit.i18nText("Fine-Design_Basic_Carton_Event_Number"), "swingWorker_" + concurrentHashMap.get(currentThreadId).hangNumber); + jsonObject.put(Toolkit.i18nText("Fine-Design_Basic_Carton_Task_Start_Time"), simpleDateFormatThreadSafe.format(concurrentHashMap.get(currentThreadId).startTime)); + jsonObject.put(Toolkit.i18nText("Fine-Design_Basic_Carton_Task_Total_Time"), runTime); + EventDispatchThreadHangMonitor.outPutJournalLog(jsonObject.toString(), SwitchForSwingChecker.EASY_CHECK_FLAG); + + } + concurrentHashMap.remove(currentThreadId); + } + + public class Checker extends TimerTask { + @Override + public void run() { + if (cartonThreadExecutorPool == null || concurrentHashMap.isEmpty()) { + return; + } + for (Map.Entry map : concurrentHashMap.entrySet()) { + map.getValue().checkForHang(); + } + } + } + + public void initTimer() { + timer = new Timer("CheckerSwingWorker",true); + timer.schedule(new Checker(), 0, CHECK_INTERVAL_MS); + } + + public void stopTimer() { + if (timer != null) { + timer.cancel(); + } + } + +} diff --git a/designer-base/src/main/java/com/fr/design/carton/CartonUploadMessage.java b/designer-base/src/main/java/com/fr/design/carton/CartonUploadMessage.java new file mode 100644 index 000000000..2c9c2baca --- /dev/null +++ b/designer-base/src/main/java/com/fr/design/carton/CartonUploadMessage.java @@ -0,0 +1,45 @@ +package com.fr.design.carton; + + +public class CartonUploadMessage { + private String hangCount; + private String slowTime; + private String threadTime; + private String info; + + public CartonUploadMessage() { + + } + + public String getHangCount() { + return hangCount; + } + + public void setHangCount(String hangCount) { + this.hangCount = hangCount; + } + + public String getSlowTime() { + return slowTime; + } + + public void setSlowTime(String slowTime) { + this.slowTime = slowTime; + } + + public String getThreadTime() { + return threadTime; + } + + public void setThreadTime(String threadTime) { + this.threadTime = threadTime; + } + + public String getInfo() { + return info; + } + + public void setInfo(String info) { + this.info = info; + } +} diff --git a/designer-base/src/main/java/com/fr/design/carton/EventDispatchThreadHangMonitor.java b/designer-base/src/main/java/com/fr/design/carton/EventDispatchThreadHangMonitor.java new file mode 100644 index 000000000..83c3f670e --- /dev/null +++ b/designer-base/src/main/java/com/fr/design/carton/EventDispatchThreadHangMonitor.java @@ -0,0 +1,393 @@ +package com.fr.design.carton; + +import com.fr.concurrent.FineExecutors; +import com.fr.json.JSONObject; +import com.fr.log.FineLoggerFactory; +import com.fr.stable.ArrayUtils; +import com.fr.stable.ProductConstantsBase; +import com.fr.stable.StableUtils; +import com.fr.stable.StringUtils; +import com.fr.third.ibm.icu.text.SimpleDateFormat; +import org.jetbrains.annotations.NotNull; + +import javax.swing.SwingUtilities; +import java.awt.EventQueue; +import java.awt.Toolkit; +import java.awt.AWTEvent; +import java.awt.event.WindowEvent; +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.lang.management.ManagementFactory; +import java.lang.management.ThreadInfo; +import java.lang.management.ThreadMXBean; +import java.util.LinkedList; +import java.util.Timer; +import java.util.TimerTask; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; + +/** + * 参考自git swinghelper + * 用于卡顿检测 + * 主要是两块内容 + * 1.获取eventQueue中每个事件的执行时间 + * 2.用一个Timer定时去检测当前执行任务的线程 + */ + +public final class EventDispatchThreadHangMonitor extends EventQueue { + /** + * 日期事件格式 + */ + private static final SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + public static final EventDispatchThreadHangMonitor INSTANCE = new EventDispatchThreadHangMonitor(); + /** + * 一个timer + */ + private Timer timer; + /** + * 开启间隔检测后两次检测的相隔时间ms + */ + private static final long CHECK_INTERVAL_MS = 100; + /** + * 最大的事件允许执行时间,超过该时间则打印堆栈等相关信息 + */ + private static final long UNREASONABLE_DISPATCH_DURATION_MS = 1500; + /** + * 事件唯一编码,用于方便日志的查看 + */ + private static long hangCount = 0; + /** + * 输出日志所在地址 + */ + private static final String JOURNAL_FILE_PATH = StableUtils.pathJoin(ProductConstantsBase.getEnvHome(), "journal_log"); + /** + * 类似于一个开关,当该值为默认的false启动时,定时任务在窗口开启前都不会对执行的事件进行检查 + */ + private boolean haveShownSomeComponent = false; + /** + * 该链表为主要的实现定时任务的容器,在重写的dispatchEvent中由pre方法将DispatchInfo加入到链表,由post方法remove + */ + private final LinkedList dispatches = new LinkedList(); + /** + * 一个变量,用于控制easy监测模式的开关 + */ + private boolean easyWitch = false; + private static ScheduledExecutorService scheduledExecutorService; + + public boolean isEasyWitch() { + return easyWitch; + } + + public void setEasyWitch(boolean easyWitch) { + this.easyWitch = easyWitch; + } + + /** + * 一个变量,用于记录Timer的开关。 + */ + + public boolean isTimerWitch() { + return timerWitch; + } + + public void setTimerWitch(boolean timerWitch) { + this.timerWitch = timerWitch; + } + + private boolean timerWitch = false; + + private synchronized static long getHangCount() { + return hangCount++; + } + + /** + * @param a can not be null + * @param b can not be null + * @return + */ + public static boolean stacksEqual(@NotNull StackTraceElement[] a, @NotNull StackTraceElement[] b) { + + if (!ArrayUtils.isSameLength(a, b)) { + return false; + } + for (int i = 0; i < a.length; ++i) { + if (!a[i].equals(b[i])) { + return false; + } + } + return true; + } + + /** + * 用于判断是不是特定的堆栈 + */ + public static boolean stackTraceElementIs(StackTraceElement e, String className, String methodName, boolean isNative) { + return e.getClassName().equals(className) && e.getMethodName().equals(methodName) && e.isNativeMethod() == isNative; + } + + /** + * 用于判断某个堆栈是否在等待另一个事件 + * 取当前堆栈前三层判断是是不是匹配等待堆栈的格式 + */ + public static boolean isWaitingForNextEvent(StackTraceElement[] currentStack) { + + return currentStack != null && currentStack.length >= 3 && + stackTraceElementIs(currentStack[0], "java.lang.Object", "wait", true) + && stackTraceElementIs(currentStack[1], "java.lang.Object", "wait", false) + && stackTraceElementIs(currentStack[2], "java.awt.EventQueue", "getNextEvent", false); + } + + /** + * event事件的包装类 + */ + public static class DispatchInfo { + // 上一次被打印的堆栈ou + private StackTraceElement[] lastReportedStack; + //获取执行该事件的线程 + private final Thread eventDispatchThread = Thread.currentThread(); + //在队列中等待执行的事件最后未执行的时间,当有一个事件执行完后就遍历dispatches给该值赋当前时间 + private long lastDispatchTimeMillis = System.currentTimeMillis(); + //事件开始的时间 + private final long startDispatchTimeMillis = System.currentTimeMillis(); + //事件编号 + private long hangNumber; + //构造函数,给当前对象赋一个递增的唯一编号 + public DispatchInfo() { + hangNumber = getHangCount(); + } + //定时调度任务检测的入口,如果执行时间大于设定的值就进入examineHang()方法 + public void checkForHang() { + if (timeSoFar() > UNREASONABLE_DISPATCH_DURATION_MS) { + examineHang(); + } + } + //超时堆栈的具体处理 + private void examineHang() { + //获取执行线程的当前堆栈 + StackTraceElement[] currentStack = eventDispatchThread.getStackTrace(); + if (isWaitingForNextEvent(currentStack)) { + return; + } + //某个事件执行时间很长,定时处理时可能会连续打很多个堆栈,对同一个事件的相同堆栈只打一次 + if (lastReportedStack !=null && stacksEqual(lastReportedStack, currentStack)) { + return; + } + String stackTrace = stackTraceToString(currentStack); + lastReportedStack = currentStack; + JSONObject jsonObject = new JSONObject(); + jsonObject.put(com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Basic_Carton_Output_Time"), simpleDateFormat.format(System.currentTimeMillis())); + jsonObject.put(com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Basic_Carton_Event_Number"), "eventQueue_" + hangNumber); + jsonObject.put(com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Basic_Carton_Duration_Task_Execute"), timeSoFar()); + jsonObject.put(com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Basic_Carton_Stack_Info"), stackTrace); + outPutJournalLog(jsonObject.toString(), SwitchForSwingChecker.TIMER_CHECK_FLAG); + checkForDeadlock(); + } + //记录连续运行了多长时间 + private long timeSoFar() { + return (System.currentTimeMillis() - lastDispatchTimeMillis); + } + //记录一个事件从被分发到结束的总运行时间 + private long totalTime() { + return (System.currentTimeMillis() - startDispatchTimeMillis); + } + //事件处理完后的时间判断 + public void dispose() { + if (timeSoFar() > UNREASONABLE_DISPATCH_DURATION_MS) { + JSONObject jsonObject = new JSONObject(); + jsonObject.put(com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Basic_Carton_Output_Time"), simpleDateFormat.format(System.currentTimeMillis())); + jsonObject.put(com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Basic_Carton_Event_Number"), "eventQueue_" + hangNumber); + jsonObject.put(com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Basic_Carton_Task_Start_Time"), simpleDateFormat.format(startDispatchTimeMillis)); + jsonObject.put(com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Basic_Carton_Task_Total_Time"), timeSoFar() + "ms"); + outPutJournalLog(jsonObject.toString(), SwitchForSwingChecker.EASY_CHECK_FLAG); + } + } + } + + public static void outPutJournalLog(String message, int flag) { + SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd"); + String date = simpleDateFormat.format(System.currentTimeMillis()); + String filename = flag == SwitchForSwingChecker.EASY_CHECK_FLAG ? SwitchForSwingChecker.EASY_CHECKER_FILE_NAME: SwitchForSwingChecker.TIMER_CHECKER_FILE_NAME; + String[] split = date.split("-"); + int month = StringUtils.isEmpty(split[1]) ? -1 : Integer.parseInt(split[1]); + String dirPath = StableUtils.pathJoin(JOURNAL_FILE_PATH, split[0], "month-" + month, date); + File dirFile = new File(dirPath); + File file = new File( StableUtils.pathJoin(dirPath, filename)); + try { + if (!file.exists()) { + if (!dirFile.exists()) { + dirFile.mkdirs(); + } + file.createNewFile(); + } + BufferedWriter bufferedWriter = new BufferedWriter(new FileWriter(file, true)); + String outputMessage = new StringBuilder(message.replaceAll("~", "\r\n")).append(",").append("\r\n").toString(); + bufferedWriter.write(outputMessage); + bufferedWriter.close(); + } catch (IOException e) { + FineLoggerFactory.getLogger().error("output fail", e); + } + } + + private EventDispatchThreadHangMonitor() { + + } + + /** + * 参考SwingExplorer,在处理模态框时没有做特殊处理,也不会输出卡顿堆栈 + * 原因是SwingExplorer窗口一直有一个监听事件,不断的add,remove。 + * 由于卡顿日志输出的是事件连续执行的时间,所以一个长时间存在的模态框被不断重复的监听事件刷新时间,就不会输出了。 + * 当检测开关打开后,在这里模拟一下监听事件,给个不耗时的任务就可以。 + */ + public void startFilterModalWindow() { + scheduledExecutorService = FineExecutors.newSingleThreadScheduledExecutor(); + scheduledExecutorService.scheduleAtFixedRate(new Runnable() { + @Override + public void run() { + SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + //不用干事,切个片就可以 + } + }); + } + }, 0, 500, TimeUnit.MILLISECONDS); + } + + public void stopFilterModalWindow() { + if (scheduledExecutorService != null) { + scheduledExecutorService.shutdown(); + } + } + /** + * Sets up a timer to check for hangs frequently. + * 初始化一个Timer + */ + public void initTimer() { + final long initialDelayMs = 0; + final boolean daemon = true; + timer = new Timer("EventDispatchThreadHangMonitor", daemon); + timer.schedule(new HangChecker(), initialDelayMs, CHECK_INTERVAL_MS); + } + + /** + * /消除Timer + */ + public void stopTimer() { + if (timer != null) { + timer.cancel(); + } + } + + /** + * /定时执行的任务 + */ + public class HangChecker extends TimerTask { + @Override + public void run() { + synchronized (dispatches) { + //如果链表为空或者窗口还没启开,定时检测就不进行 + if (dispatches.isEmpty() || !haveShownSomeComponent) { + return; + } + dispatches.getLast().checkForHang(); + } + } + } + + /** + * 将swing中默认的EventQueue换成自己的 + */ + public static void initMonitoring() { + Toolkit.getDefaultToolkit().getSystemEventQueue().push(INSTANCE); + } + + /** + * Overrides EventQueue.dispatchEvent to call our pre and post hooks either + * side of the system's event dispatch code. + * 重写 + */ + @Override + protected void dispatchEvent(AWTEvent event) { + //如果两个开关都没开,那就不走重写方法了 + if (!isEasyWitch() && !isTimerWitch()) { + super.dispatchEvent(event); + } else { + try { + preDispatchEvent(); + super.dispatchEvent(event); + } finally { + postDispatchEvent(); + if (!haveShownSomeComponent && + event instanceof WindowEvent && event.getID() == WindowEvent.WINDOW_OPENED) { + haveShownSomeComponent = true; + } + } + } + } + + /** + * Starts tracking a dispatch. + */ + private synchronized void preDispatchEvent() { + synchronized (dispatches) { + dispatches.addLast(new DispatchInfo()); + } + } + + /** + * Stops tracking a dispatch. + */ + private synchronized void postDispatchEvent() { + synchronized (dispatches) { + DispatchInfo justFinishedDispatch = dispatches.removeLast(); + if (isEasyWitch()) { + justFinishedDispatch.dispose(); + } + //嵌套最深的事件执行完毕后刷新链表中其他事件的lastDispatchTimeMillis + Thread currentEventDispatchThread = Thread.currentThread(); + for (DispatchInfo dispatchInfo : dispatches) { + if (dispatchInfo.eventDispatchThread == currentEventDispatchThread) { + dispatchInfo.lastDispatchTimeMillis = System.currentTimeMillis(); + } + } + } + } + + /** + * 检查死锁 + */ + public static void checkForDeadlock() { + ThreadMXBean threadBean = ManagementFactory.getThreadMXBean(); + long[] threadIds = threadBean.findDeadlockedThreads(); + if (threadIds == null) { + return; + } + FineLoggerFactory.getLogger().warn("deadlock detected involving the following threads:"); + ThreadInfo[] threadInfos = threadBean.getThreadInfo(threadIds, Integer.MAX_VALUE); + for (ThreadInfo info : threadInfos) { + FineLoggerFactory.getLogger().warn("Thread # {} {} ( {} ) waiting on {} held by {} {}", info.getThreadId(), info.getThreadName(), + info.getThreadState(), info.getLockName(), info.getLockOwnerName(), stackTraceToStringForConsole(info.getStackTrace())); + } + } + + public static String stackTraceToString(StackTraceElement[] stackTrace) { + StringBuilder result = new StringBuilder(); + for (StackTraceElement stackTraceElement : stackTrace) { + String indentation = " "; + result.append("~").append(indentation).append(stackTraceElement); + } + return result.toString(); + } + + public static String stackTraceToStringForConsole(StackTraceElement[] stackTrace) { + StringBuilder result = new StringBuilder(); + for (StackTraceElement stackTraceElement : stackTrace) { + String indentation = " "; + result.append("\r\n").append(indentation).append(stackTraceElement); + } + return result.toString(); + } + +} \ No newline at end of file diff --git a/designer-base/src/main/java/com/fr/design/carton/FeedbackToolboxDialog.java b/designer-base/src/main/java/com/fr/design/carton/FeedbackToolboxDialog.java new file mode 100644 index 000000000..e05d3626a --- /dev/null +++ b/designer-base/src/main/java/com/fr/design/carton/FeedbackToolboxDialog.java @@ -0,0 +1,336 @@ +package com.fr.design.carton; + +import com.fr.decision.webservice.v10.log.download.utils.LogZipUtils; +import com.fr.design.DesignerEnvManager; +import com.fr.design.constants.UIConstants; +import com.fr.design.dialog.FineJOptionPane; +import com.fr.design.env.DesignerWorkspaceInfo; +import com.fr.design.gui.date.UIDatePicker; +import com.fr.design.gui.ibutton.UIButton; +import com.fr.design.gui.icheckbox.UICheckBox; +import com.fr.design.gui.ilable.UILabel; +import com.fr.design.i18n.Toolkit; +import com.fr.design.layout.FRGUIPaneFactory; +import com.fr.design.mainframe.DesignerContext; +import com.fr.design.utils.gui.GUICoreUtils; +import com.fr.env.detect.ui.EnvDetectorDialog; +import com.fr.file.FILE; +import com.fr.file.FILEChooserPane; +import com.fr.file.filter.ChooseFileFilter; +import com.fr.general.GeneralUtils; +import com.fr.log.FineLoggerFactory; +import com.fr.stable.StableUtils; +import com.fr.stable.StringUtils; + +import javax.swing.*; +import java.awt.*; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.awt.event.WindowEvent; +import java.io.File; +import java.text.ParseException; +import java.util.List; + + + +public class FeedbackToolboxDialog extends JDialog { + private UIDatePicker uiDatePicker; + private JPanel generalSettingPanel = null; + private UICheckBox easyCheckerButton = null; + private UICheckBox timerCheckerButton = null; + private UIButton uploadButton = null; + private UILabel exportLogLabel = null; + private final Color backgroundColor = new Color(240, 240, 243, 1); + private final Color lineColor = new Color(192, 192, 192, 120); + private JPanel body = null; + private static final String WORK_SPACE_PATH = "reportlets"; + + public FeedbackToolboxDialog(Frame owner) { + super(owner, Toolkit.i18nText("Fine-Design_Basic_Carton_Feedback_ToolBox")); + setResizable(false); + this.setLayout(FRGUIPaneFactory.createBorderLayout()); + createBodyPanel(); + add(body); + setSize(body.getPreferredSize()); + setSwitches(!StringUtils.isEmpty(GeneralUtils.objectToString(uiDatePicker.getSelectedItem()))); + repaint(); + GUICoreUtils.centerWindow(this); + } + + public void createBodyPanel() { + JPanel body = FRGUIPaneFactory.createBorderLayout_L_Pane(); + body.setBackground(backgroundColor); + JPanel titlePane = createTitlePane(); + JPanel tailPane = createTailPane(); + JPanel midPane = createMidPane(); + JPanel infoPane = createInfoPane(); + body.add(titlePane, BorderLayout.NORTH); + body.add(tailPane, BorderLayout.SOUTH); + body.add(midPane, BorderLayout.CENTER); + midPane.add(infoPane, BorderLayout.NORTH); + Dimension dimension = new Dimension(662, 556); + body.setPreferredSize(dimension); + this.body = body; + } + + private JPanel createInfoPane() { + JPanel northPane = FRGUIPaneFactory.createNColumnGridInnerContainer_Pane(2, 10, 10); + UILabel title = new UILabel(); + //空格布局会好看一点 + title.setText(" " + Toolkit.i18nText("Fine-Design_Basic_Carton_Record_Lag_Time") + ": "); + //判断一下当天是否有卡顿日志记录,如果有将日期设置为当天,如果没有设置为空 + boolean cartonExists = SwitchForSwingChecker.isCartonExists(); + if (cartonExists) { + this.uiDatePicker = new UIDatePicker(UIDatePicker.STYLE_CN_DATE1, this); + } else { + this.uiDatePicker = new UIDatePicker(UIDatePicker.STYLE_CN_DATE1, null, this); + } + Dimension dimension = new Dimension(160, 100); + uiDatePicker.setPreferredSize(dimension); + northPane.add(GUICoreUtils.createFlowPane(new Component[]{title, uiDatePicker}, FlowLayout.LEFT)); + exportLogLabel = new UILabel(); + exportLogLabel.setText(Toolkit.i18nText("Fine-Design_Basic_Carton_Export_Carton_Log")); + exportLogLabel.setForeground(UIConstants.FLESH_BLUE); + exportLogLabel.addMouseListener(new MouseAdapter() { + @Override + public void mousePressed(MouseEvent e) { + if (exportLogLabel.isEnabled()) { + exportLogFile(); + } + } + + @Override + public void mouseEntered(MouseEvent evt) { + Object source = evt.getSource(); + if (source instanceof UILabel) { + ((UILabel) source).setCursor(new Cursor(Cursor.HAND_CURSOR)); + } + } + }); + + northPane.add(GUICoreUtils.createFlowPane(exportLogLabel, FlowLayout.RIGHT)); + return northPane; + } + + private void exportLogFile() { + String selectDate = GeneralUtils.objectToString(uiDatePicker.getSelectedItem()); + FILEChooserPane fileChooserPane = FILEChooserPane.getInstance(); + StringBuilder fileName = new StringBuilder(); + fileName.append(selectDate).append(Toolkit.i18nText("Fine-Design_Basic_Carton_Carton_Log")); + fileChooserPane.setFileNameTextField(fileName.toString(), " "); + fileChooserPane.removeAllFilter(); + fileChooserPane.addChooseFILEFilter(new ChooseFileFilter("zip", Toolkit.i18nText("Fine-Design_Basic_Carton_Compile_File"))); + int chooseResult = fileChooserPane.showSaveDialog(DesignerContext.getDesignerFrame()); + if (chooseResult == 0) { + FILE selectedFile = fileChooserPane.getSelectedFILE(); + String path = selectedFile.getPath(); + //selectDate 2002-03-09例子 + String[] split = selectDate.split("-"); + int month = Integer.parseInt(split[1]); + File sourceFile = new File(StableUtils.pathJoin(SwitchForSwingChecker.JOURNAL_FILE_PATH, split[0], "month-" + month, selectDate)); + if (sourceFile.exists()) { + File[] files = sourceFile.listFiles(); + if (files != null) { + try { + if (path.startsWith(WORK_SPACE_PATH)) { + String curEnvName = DesignerEnvManager.getEnvManager().getCurEnvName(); + DesignerWorkspaceInfo workspaceInfo = DesignerEnvManager.getEnvManager().getWorkspaceInfo(curEnvName); + String workspaceInfoPath = workspaceInfo.getPath(); + path = new StringBuilder(workspaceInfoPath).append(path.substring(10)).toString(); + } + LogZipUtils.compress(files, path, false); + FineJOptionPane.showMessageDialog(null, Toolkit.i18nText("Fine-Design_Report_Exported_Successfully")); + } catch (Exception exception) { + FineJOptionPane.showMessageDialog(null, Toolkit.i18nText("Fine-Design_Report_Export_Failed"), UIManager.getString("OptionPane.messageDialogTitle"), JOptionPane.ERROR_MESSAGE); + FineLoggerFactory.getLogger().error("export file fail", exception); + } + } + } + fileChooserPane.removeAllFilter(); + } + } + + private JPanel createTailPane() { + JPanel tailPanel = FRGUIPaneFactory.createBorderLayout_S_Pane(); + tailPanel.setBorder(BorderFactory.createMatteBorder(1, 0, 0, 0, lineColor)); + JPanel actionsPanel = FRGUIPaneFactory.createBorderLayout_S_Pane(); + actionsPanel.setLayout(FRGUIPaneFactory.createM_BorderLayout()); + { + uploadButton = new UIButton(Toolkit.i18nText("Fine-Design_Basic_Carton_Upload_Carton_Log")); + uploadButton.addActionListener((e) -> { + try { + List list = SwitchForSwingChecker.uploadJournalLog(uiDatePicker.getSelectedDate()); + if (list.isEmpty()) { + FineJOptionPane.showMessageDialog(null, Toolkit.i18nText("Fine_Design_Basic_Upload_Fail"), UIManager.getString("OptionPane.messageDialogTitle"), JOptionPane.ERROR_MESSAGE); + } else { + FineJOptionPane.showMessageDialog(null, Toolkit.i18nText("Fine-Design_Basic_Upload_Success")); + } + } catch (ParseException parseException) { + FineLoggerFactory.getLogger().error("parse error", parseException); + } + + }); + actionsPanel.add(uploadButton, BorderLayout.WEST); + + UIButton cancelButton = new UIButton(Toolkit.i18nText("Fine-Design_Basic_Cancel")); + cancelButton.addActionListener((e) -> { + setVisible(false); + EnvDetectorDialog envDetectorDialog = new EnvDetectorDialog(DesignerContext.getDesignerFrame()); + envDetectorDialog.setVisible(true); + dispose(); + }); + actionsPanel.add(cancelButton, BorderLayout.EAST); + } + UIButton currencySetButton = new UIButton(Toolkit.i18nText("Fine-Design_Basic_Carton_General_Settings")); + currencySetButton.addActionListener((e -> { + createGeneralSettingPanel(); + this.remove(body); + this.add(generalSettingPanel); + setSize(generalSettingPanel.getPreferredSize()); + repaint(); + setVisible(true); + })); + tailPanel.add(actionsPanel, BorderLayout.EAST); + tailPanel.add(currencySetButton, BorderLayout.WEST); + return tailPanel; + } + + private JPanel createTitlePane() { + JPanel titlePane = FRGUIPaneFactory.createBorderLayout_M_Pane(); + titlePane.setBorder(BorderFactory.createMatteBorder(0, 0, 1, 0, lineColor)); + UILabel uiLabel = new UILabel(Toolkit.i18nText("Fine-Design_Basic_Carton_Carton_Journal_Record")); + uiLabel.setForeground(UIConstants.FLESH_BLUE); + titlePane.add(uiLabel, BorderLayout.WEST); + return titlePane; + } + + private JPanel createMidPane() { + JPanel midPanel = FRGUIPaneFactory.createBorderLayout_L_Pane(); + return midPanel; + } + + /** + * 下面是通用设置的面板 + */ + private void createGeneralSettingPanel() { + JPanel generalSettingPanel = FRGUIPaneFactory.createBorderLayout_L_Pane(); + JPanel tailPaneInGeneralSettings = createTailPaneInGeneralSettings(); + generalSettingPanel.add(tailPaneInGeneralSettings, BorderLayout.SOUTH); + + JPanel titlePaneInGeneralSettings = createTitlePaneInGeneralSettings(); + generalSettingPanel.add(titlePaneInGeneralSettings, BorderLayout.NORTH); + + JPanel midPanel = FRGUIPaneFactory.createBorderLayout_L_Pane(); + generalSettingPanel.add(midPanel, BorderLayout.CENTER); + + JPanel infoPane = createInfoPanelInGeneralSettings(); + midPanel.add(infoPane, BorderLayout.NORTH); + + Dimension dimension = new Dimension(662, 556); + generalSettingPanel.setPreferredSize(dimension); + generalSettingPanel.setBackground(backgroundColor); + this.generalSettingPanel = generalSettingPanel; + } + + private JPanel createTitlePaneInGeneralSettings() { + JPanel titlePane = FRGUIPaneFactory.createBorderLayout_L_Pane(); + titlePane.setBorder(BorderFactory.createMatteBorder(0, 0, 1, 0, lineColor)); + UILabel uiLabel = new UILabel(Toolkit.i18nText("Fine-Design_Basic_Carton_Carton_Journal_Record") + "/"); + uiLabel.addMouseListener(new MouseAdapter() { + @Override + public void mouseClicked(MouseEvent e) { + createBodyPanel(); + remove(generalSettingPanel); + add(body); + setPreferredSize(body.getPreferredSize()); + setSwitches(!StringUtils.isEmpty(GeneralUtils.objectToString(uiDatePicker.getSelectedItem()))); + repaint(); + setVisible(true); + } + + @Override + public void mouseEntered(MouseEvent evt) { + Object source = evt.getSource(); + if (source instanceof UILabel) { + ((UILabel) source).setCursor(new Cursor(Cursor.HAND_CURSOR)); + } + } + }); + UILabel uiCurrentLabel = new UILabel(Toolkit.i18nText("Fine-Design_Basic_Carton_General_Settings")); + uiCurrentLabel.setForeground(UIConstants.FLESH_BLUE); + titlePane.add(GUICoreUtils.createFlowPane(new Component[]{uiLabel, uiCurrentLabel}, FlowLayout.LEFT)); + return titlePane; + } + + private JPanel createTailPaneInGeneralSettings() { + JPanel tailPanel = FRGUIPaneFactory.createBorderLayout_S_Pane(); + tailPanel.setBorder(BorderFactory.createMatteBorder(1, 0, 0, 0, lineColor)); + JPanel actionsPanel = FRGUIPaneFactory.createBorderLayout_S_Pane(); + actionsPanel.setLayout(FRGUIPaneFactory.createM_BorderLayout()); + { + UIButton confirmButton = new UIButton(Toolkit.i18nText("Fine-Design_Basic_Save")); + confirmButton.addActionListener((e) -> { + if (easyCheckerButton.isSelected()) { + SwitchForSwingChecker.startEasyChecker(); + } else { + SwitchForSwingChecker.stopEasyChecker(); + } + if (timerCheckerButton.isSelected()) { + SwitchForSwingChecker.startTimerChecker(); + } else { + SwitchForSwingChecker.stopTimerChecker(); + } + createBodyPanel(); + remove(generalSettingPanel); + add(body); + setPreferredSize(body.getPreferredSize()); + setSwitches(!StringUtils.isEmpty(GeneralUtils.objectToString(uiDatePicker.getSelectedItem()))); + repaint(); + setVisible(true); + }); + actionsPanel.add(confirmButton, BorderLayout.WEST); + + UIButton cancelButton = new UIButton(Toolkit.i18nText("Fine-Design_Basic_Cancel")); + cancelButton.addActionListener((e) -> { + createBodyPanel(); + remove(generalSettingPanel); + add(body); + setPreferredSize(body.getPreferredSize()); + repaint(); + setVisible(true); + + }); + actionsPanel.add(cancelButton, BorderLayout.EAST); + } + tailPanel.add(actionsPanel, BorderLayout.EAST); + return tailPanel; + } + + private JPanel createInfoPanelInGeneralSettings() { + JPanel infoPane = FRGUIPaneFactory.createNColumnGridInnerContainer_S_Pane(1); + easyCheckerButton = new UICheckBox(Toolkit.i18nText("Fine-Design_Basic_Carton_Operation_Time_Consuming_Detection")); + timerCheckerButton = new UICheckBox(Toolkit.i18nText("Fine-Design_Basic_Carton_Carton_Operation_Class_Capture")); + easyCheckerButton.setSelected(SwitchForSwingChecker.isEasyChecker()); + timerCheckerButton.setSelected(SwitchForSwingChecker.isCheckerTimerSwitch()); + infoPane.add(GUICoreUtils.createFlowPane(easyCheckerButton, FlowLayout.LEFT)); + infoPane.add(GUICoreUtils.createFlowPane(timerCheckerButton, FlowLayout.LEFT)); + return infoPane; + } + + @Override + protected void processWindowEvent(WindowEvent e) { + super.processWindowEvent(e); + if (e.getID() == WindowEvent.WINDOW_CLOSING) { + EnvDetectorDialog envDetectorDialog = new EnvDetectorDialog(DesignerContext.getDesignerFrame()); + envDetectorDialog.setVisible(true); + } + } + + /** + * 上传和导出卡顿日志的可用化处理,如果没有选择日期就不可用 + */ + public void setSwitches(boolean flag) { + uploadButton.setEnabled(flag); + exportLogLabel.setEnabled(flag); + } +} diff --git a/designer-base/src/main/java/com/fr/design/carton/MonthlyCartonFile.java b/designer-base/src/main/java/com/fr/design/carton/MonthlyCartonFile.java new file mode 100644 index 000000000..f9118d3e1 --- /dev/null +++ b/designer-base/src/main/java/com/fr/design/carton/MonthlyCartonFile.java @@ -0,0 +1,36 @@ +package com.fr.design.carton; + +import java.io.File; + +public class MonthlyCartonFile { + private File currentMonthFile; + private File lastMonthFile; + private File nextMonthFile; + public MonthlyCartonFile() { + + } + + public File getCurrentMonthFile() { + return currentMonthFile; + } + + public void setCurrentMonthFile(File currentMonthFile) { + this.currentMonthFile = currentMonthFile; + } + + public File getLastMonthFile() { + return lastMonthFile; + } + + public void setLastMonthFile(File lastMonthFile) { + this.lastMonthFile = lastMonthFile; + } + + public File getNextMonthFile() { + return nextMonthFile; + } + + public void setNextMonthFile(File nextMonthFile) { + this.nextMonthFile = nextMonthFile; + } +} diff --git a/designer-base/src/main/java/com/fr/design/carton/SwitchForSwingChecker.java b/designer-base/src/main/java/com/fr/design/carton/SwitchForSwingChecker.java new file mode 100644 index 000000000..ec1b427dd --- /dev/null +++ b/designer-base/src/main/java/com/fr/design/carton/SwitchForSwingChecker.java @@ -0,0 +1,311 @@ +package com.fr.design.carton; + + +import com.fr.design.i18n.Toolkit; +import com.fr.general.GeneralUtils; +import com.fr.json.JSON; +import com.fr.json.JSONArray; +import com.fr.json.JSONObject; +import com.fr.log.FineLoggerFactory; +import com.fr.stable.ProductConstantsBase; +import com.fr.stable.StableUtils; +import com.fr.stable.StringUtils; +import com.fr.stable.xml.XMLPrintWriter; +import com.fr.stable.xml.XMLReadable; +import com.fr.stable.xml.XMLWriter; +import com.fr.stable.xml.XMLableReader; +import sun.awt.AppContext; + +import javax.swing.SwingWorker; +import java.io.IOException; +import java.io.File; +import java.io.BufferedReader; +import java.io.FileReader; +import java.text.SimpleDateFormat; +import java.util.List; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Map; +import java.util.Date; +import java.util.Calendar; + +public class SwitchForSwingChecker implements XMLReadable, XMLWriter { + /** + * Designer4Debug类名 + */ + private static final String DEBUG_MAIN_CLASS_NAME = "com.fr.start.Designer4Debug"; + /** + * XML标签 + */ + public static final String XML_TAG = "SwitchForSwingChecker"; + /** + * 定时任务的开关 + */ + private static boolean checkerTimerSwitch = false; + /** + * 简单记录事件执行时间的开关 + */ + private static boolean easyChecker = false; + /** + * 一个标识位用于区分耗时任务时长检测(简单检测)和timer检测 + */ + public static final int TIMER_CHECK_FLAG = 0; + public static final int EASY_CHECK_FLAG = 1; + + /** + * 日志存储地址 + */ + public static final String JOURNAL_FILE_PATH = StableUtils.pathJoin(ProductConstantsBase.getEnvHome(), "journal_log"); + public static final String EASY_CHECKER_FILE_NAME = "easy_check_log.csv"; + public static final String TIMER_CHECKER_FILE_NAME = "timer_check_log.csv"; + public static boolean isCheckerTimerSwitch() { + return checkerTimerSwitch; + } + + public static boolean isEasyChecker() { + return easyChecker; + } + + public static volatile SwitchForSwingChecker switchForSwingChecker = new SwitchForSwingChecker(); + + public static SwitchForSwingChecker getInstance() { + return switchForSwingChecker; + } + + public static void startTimerChecker() { + if (!checkerTimerSwitch) { + EventDispatchThreadHangMonitor.INSTANCE.initTimer(); + CartonThreadExecutorPool.getTimerThreadExecutorPool().initTimer(); + EventDispatchThreadHangMonitor.INSTANCE.setTimerWitch(true); + checkerTimerSwitch = true; + if (!easyChecker) { + EventDispatchThreadHangMonitor.INSTANCE.startFilterModalWindow(); + } + } + } + + public static void stopTimerChecker() { + if (checkerTimerSwitch) { + EventDispatchThreadHangMonitor.INSTANCE.stopTimer(); + CartonThreadExecutorPool.getTimerThreadExecutorPool().stopTimer(); + EventDispatchThreadHangMonitor.INSTANCE.setTimerWitch(false); + checkerTimerSwitch = false; + if (!easyChecker) { + EventDispatchThreadHangMonitor.INSTANCE.stopFilterModalWindow(); + } + } + } + + public static void startEasyChecker() { + if (!easyChecker) { + EventDispatchThreadHangMonitor.INSTANCE.setEasyWitch(true); + CartonThreadExecutorPool.getTimerThreadExecutorPool().setEasyWitch(true); + easyChecker = true; + if (!checkerTimerSwitch) { + EventDispatchThreadHangMonitor.INSTANCE.startFilterModalWindow(); + } + } + } + + public static void stopEasyChecker() { + if (easyChecker) { + EventDispatchThreadHangMonitor.INSTANCE.setEasyWitch(false); + CartonThreadExecutorPool.getTimerThreadExecutorPool().setEasyWitch(false); + easyChecker = false; + if (!checkerTimerSwitch) { + EventDispatchThreadHangMonitor.INSTANCE.stopFilterModalWindow(); + } + } + } + + /** + * 获取文件名字以及判断文件是否存在 + */ + private static CartonFiles getFiles(String date) { + String[] split = date.split("-"); + int month = StringUtils.isEmpty(split[1]) ? -1 : Integer.parseInt(split[1]); + String dirPath = StableUtils.pathJoin(JOURNAL_FILE_PATH, split[0], "month-" + month, date); + File file1 = new File(StableUtils.pathJoin(dirPath, EASY_CHECKER_FILE_NAME)); + File file2 = new File(StableUtils.pathJoin(dirPath, TIMER_CHECKER_FILE_NAME)); + File[] files = new File[2]; + files[0] = file1; + files[1] = file2; + CartonFiles cartonFiles = new CartonFiles(); + cartonFiles.setEasyCheckerFile(file1); + cartonFiles.setTimerCheckerFile(file2); + return cartonFiles; + } + + /** + *处理文件 + * 一共四种情况, + * 两个文件都不存在 + * 文件一存在,文件二不存在 + * 文件二存在,文件一不存在 + * 两个文件都存在 + */ + private static List getCartonLog(File easyFile, File timerFile) { + List res = new ArrayList<>(); + List easyFileCartonLog = getEasyFileCartonLog(easyFile); + List timerFileCartonLog = getTimerFileCartonLog(timerFile); + Map easyFileMap = new HashMap<>(); + for (CartonUploadMessage cartonUploadMessage : easyFileCartonLog) { + easyFileMap.put(cartonUploadMessage.getHangCount(), cartonUploadMessage); + res.add(cartonUploadMessage); + } + for (CartonUploadMessage cartonUploadMessage : timerFileCartonLog) { + String hangCount = cartonUploadMessage.getHangCount(); + if (easyFileMap.containsKey(hangCount)) { + cartonUploadMessage.setThreadTime(easyFileMap.get(hangCount).getThreadTime()); + } + res.add(cartonUploadMessage); + } + return res; + } + + private static List getTimerFileCartonLog(File file) { + List res = new ArrayList<>(); + try { + StringBuilder stringBuilder = new StringBuilder(); + stringBuilder.append("["); + BufferedReader bufferedReader1 = new BufferedReader(new FileReader(file)); + String line1; + while ((line1 = bufferedReader1.readLine()) != null) { + stringBuilder.append(line1); + } + bufferedReader1.close(); + stringBuilder.append("]"); + JSONArray easyCheckerJSON = JSON.ARRAY.createJSON(GeneralUtils.objectToString(stringBuilder)); + for (Object jsonObject : easyCheckerJSON) { + CartonUploadMessage cartonUploadMessage = new CartonUploadMessage(); + JSONObject x = (JSONObject) jsonObject; + cartonUploadMessage.setHangCount(x.getString(Toolkit.i18nText("Fine-Design_Basic_Carton_Event_Number"))); + cartonUploadMessage.setSlowTime(x.getString(Toolkit.i18nText("Fine-Design_Basic_Carton_Output_Time")) + "ms"); + cartonUploadMessage.setThreadTime("undefined"); + //这个跟输出到文件中的格式匹配,参考EventDis里的stackTraceToString方法 + String indentation = " "; + String logMessage = x.getString(Toolkit.i18nText("Fine-Design_Basic_Carton_Stack_Info")).replaceAll(indentation, "\r\n "); + cartonUploadMessage.setInfo(logMessage); + res.add(cartonUploadMessage); + } + } catch (IOException e) { + FineLoggerFactory.getLogger().error("upload fail", e); + } + return res; + } + + private static List getEasyFileCartonLog(File file) { + List res = new ArrayList<>(); + try { + StringBuilder stringBuilder = new StringBuilder(); + stringBuilder.append("["); + BufferedReader bufferedReader1 = new BufferedReader(new FileReader(file)); + String line1; + while ((line1 = bufferedReader1.readLine()) != null) { + stringBuilder.append(line1); + } + bufferedReader1.close(); + stringBuilder.append("]"); + JSONArray timerCheckerJSON = JSON.ARRAY.createJSON(GeneralUtils.objectToString(stringBuilder)); + for (Object jsonObject : timerCheckerJSON) { + JSONObject x = (JSONObject) jsonObject; + CartonUploadMessage cartonUploadMessage = new CartonUploadMessage(); + cartonUploadMessage.setHangCount(x.getString(Toolkit.i18nText("Fine-Design_Basic_Carton_Event_Number"))); + cartonUploadMessage.setSlowTime(x.getString(Toolkit.i18nText("Fine-Design_Basic_Carton_Output_Time"))); + cartonUploadMessage.setThreadTime(x.getString(Toolkit.i18nText("Fine-Design_Basic_Carton_Task_Total_Time"))); + cartonUploadMessage.setInfo("undefined"); + res.add(cartonUploadMessage); + } + } catch (IOException e) { + FineLoggerFactory.getLogger().error("upload fail", e); + } + return res; + } + + /** + * /埋点方法上传卡顿信息入口 + date为 2022-09-08的格式 + */ + public static List uploadJournalLog(Date dateTime) { + List res = new ArrayList<>(); + SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd"); + CartonFiles files = getFiles(simpleDateFormat.format(dateTime)); + File easyCheckerFile = files.getEasyCheckerFile(); + File timerCheckerFile = files.getTimerCheckerFile(); + if (easyCheckerFile.exists() && timerCheckerFile.exists()) { + return getCartonLog(easyCheckerFile, timerCheckerFile); + } else if (easyCheckerFile.exists()) { + return getEasyFileCartonLog(easyCheckerFile); + } else if (timerCheckerFile.exists()) { + return getTimerFileCartonLog(timerCheckerFile); + } else { + return res; + } + } + + /** + * 初始化监控任务,主要是替换EventQueue以及SwingWorker执行任务的线程池 + * + */ + public static void initThreadMonitoring () { + String mainClass = System.getProperty("sun.java.command"); + //判断一下,如果是以Designer4Debug启动,就不注册代码,不然会覆盖掉SwingExplorer,导致其无法使用 + if (!StringUtils.equals(mainClass, DEBUG_MAIN_CLASS_NAME)) { + EventDispatchThreadHangMonitor.initMonitoring(); + AppContext.getAppContext().put(SwingWorker.class, CartonThreadExecutorPool.getTimerThreadExecutorPool()); + } + } + + /** + * 判断是否有指定日期的卡顿日志,没有就返回false + */ + public static boolean isCartonExists(Date date) { + SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd"); + String format = simpleDateFormat.format(date); + Calendar calendar = Calendar.getInstance(); + int month = calendar.get(Calendar.MONTH) + 1; + int year = calendar.get(Calendar.YEAR); + File file = new File(StableUtils.pathJoin(JOURNAL_FILE_PATH, String.valueOf(year), "month-" + month, format)); + return file.exists(); + } + + public static boolean isCartonExists() { + return isCartonExists(new Date()); + } + + private void initSwitchChecker() { + if (easyChecker) { + EventDispatchThreadHangMonitor.INSTANCE.setEasyWitch(true); + CartonThreadExecutorPool.getTimerThreadExecutorPool().setEasyWitch(true); + } + if (checkerTimerSwitch) { + EventDispatchThreadHangMonitor.INSTANCE.initTimer(); + CartonThreadExecutorPool.getTimerThreadExecutorPool().initTimer(); + EventDispatchThreadHangMonitor.INSTANCE.setTimerWitch(true); + } + if (easyChecker || checkerTimerSwitch) { + EventDispatchThreadHangMonitor.INSTANCE.startFilterModalWindow(); + } + } + + @Override + public void readXML(XMLableReader reader) { + if (reader.isAttr()) { + checkerTimerSwitch = reader.getAttrAsBoolean("checkerTimerSwitch", false); + easyChecker = reader.getAttrAsBoolean("easyChecker", false); + } + try { + initSwitchChecker(); + } catch (Throwable t) { + FineLoggerFactory.getLogger().error("read checker attr fail", t); + } + } + + @Override + public void writeXML(XMLPrintWriter writer) { + writer.startTAG(XML_TAG); + writer.attr("checkerTimerSwitch", checkerTimerSwitch); + writer.attr("easyChecker", easyChecker); + writer.end(); + } +} diff --git a/designer-base/src/main/java/com/fr/design/components/tooltip/ModernToolTip.java b/designer-base/src/main/java/com/fr/design/components/tooltip/ModernToolTip.java index 0a01f8bbf..5125c0027 100644 --- a/designer-base/src/main/java/com/fr/design/components/tooltip/ModernToolTip.java +++ b/designer-base/src/main/java/com/fr/design/components/tooltip/ModernToolTip.java @@ -12,11 +12,9 @@ import javax.swing.plaf.ToolTipUI; import java.awt.Color; import java.awt.Dimension; import java.awt.FontMetrics; -import java.awt.GradientPaint; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.RenderingHints; -import java.awt.geom.GeneralPath; import java.io.BufferedReader; import java.io.IOException; import java.io.StringReader; @@ -52,7 +50,7 @@ public class ModernToolTip extends UIToolTip { Graphics2D g2 = (Graphics2D) g; g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); g2.setColor(new Color(51, 51, 52, (int) Math.round(0.7 * 255))); - g2.fillRoundRect(0, 0, width, height, 0, 0); + g2.fillRoundRect(0, 0, width, height, 4, 4); g2.setColor(Color.WHITE); if (strs != null) { diff --git a/designer-base/src/main/java/com/fr/design/data/BasicTableDataTreePane.java b/designer-base/src/main/java/com/fr/design/data/BasicTableDataTreePane.java index f45e84f20..3dbcf8187 100644 --- a/designer-base/src/main/java/com/fr/design/data/BasicTableDataTreePane.java +++ b/designer-base/src/main/java/com/fr/design/data/BasicTableDataTreePane.java @@ -2,9 +2,8 @@ package com.fr.design.data; import com.fr.base.BaseUtils; import com.fr.base.TableData; +import com.fr.data.MultiResultTableData; import com.fr.data.TableDataSource; -import com.fr.data.api.StoreProcedureAssist; -import com.fr.data.impl.storeproc.StoreProcedure; import com.fr.design.DesignModelAdapter; import com.fr.design.actions.UpdateAction; import com.fr.design.data.datapane.TableDataCreatorProducer; @@ -13,7 +12,7 @@ import com.fr.design.data.datapane.TableDataSourceOP; import com.fr.design.data.datapane.TableDataTree; import com.fr.design.data.tabledata.ResponseDataSourceChange; import com.fr.design.data.tabledata.tabledatapane.AbstractTableDataPane; -import com.fr.design.data.tabledata.wrapper.StoreProcedureDataWrapper; +import com.fr.design.data.tabledata.wrapper.MultiResultTableDataWrapper; import com.fr.design.data.tabledata.wrapper.TableDataWrapper; import com.fr.design.data.tabledata.wrapper.TemplateTableDataWrapper; import com.fr.design.dialog.BasicDialog; @@ -184,7 +183,7 @@ public abstract class BasicTableDataTreePane extends DockingView implements Resp private boolean isIncludeUnderline(String name) { - return ComparatorUtils.equals(name.indexOf(StoreProcedureAssist.GROUP_MARKER), -1) ? false : true; + return ComparatorUtils.equals(name.indexOf(MultiResultTableData.GROUP_MARKER), -1) ? false : true; } public abstract void addDataPane(final AbstractTableDataPane uPanel, String paneName); @@ -441,16 +440,16 @@ public abstract class BasicTableDataTreePane extends DockingView implements Resp data = selectedNO.getObject(); } try { - if (((TableDataWrapper) Objects.requireNonNull(data)).getTableData() instanceof StoreProcedure) { - ((StoreProcedure) (((TableDataWrapper) data).getTableData())).resetDataModelList(); - if (data instanceof StoreProcedureDataWrapper) { - StoreProcedureDataWrapper oldSdw = ((StoreProcedureDataWrapper) data); - StoreProcedureDataWrapper newSdw = new StoreProcedureDataWrapper((StoreProcedure) oldSdw.getTableData(), oldSdw.getStoreprocedureName(), oldSdw.getTableDataName()); - newSdw.previewData(StoreProcedureDataWrapper.PREVIEW_ONE); + if (((TableDataWrapper) Objects.requireNonNull(data)).getTableData() instanceof MultiResultTableData) { + ((MultiResultTableData) (((TableDataWrapper) data).getTableData())).resetDataModelList(); + if (data instanceof MultiResultTableDataWrapper) { + MultiResultTableDataWrapper oldSdw = ((MultiResultTableDataWrapper) data); + MultiResultTableDataWrapper newSdw = new MultiResultTableDataWrapper((MultiResultTableData) oldSdw.getTableData(), oldSdw.getTableDataName(), oldSdw.getDataModelName()); + newSdw.previewData(MultiResultTableDataWrapper.PREVIEW_ONE); } else { - StoreProcedure storeProcedure = (StoreProcedure) ((TableDataWrapper) data).getTableData(); - StoreProcedureDataWrapper storeProcedureDataWrapper = new StoreProcedureDataWrapper(storeProcedure, StringUtils.EMPTY, StringUtils.EMPTY); - storeProcedureDataWrapper.previewData(StoreProcedureDataWrapper.PREVIEW_ALL); + MultiResultTableData tableData = (MultiResultTableData) ((TableDataWrapper) data).getTableData(); + MultiResultTableDataWrapper storeProcedureDataWrapper = new MultiResultTableDataWrapper(tableData, StringUtils.EMPTY, StringUtils.EMPTY); + storeProcedureDataWrapper.previewData(MultiResultTableDataWrapper.PREVIEW_ALL); } } else { ((TableDataWrapper) data).previewData(); diff --git a/designer-base/src/main/java/com/fr/design/data/DesignTableDataManager.java b/designer-base/src/main/java/com/fr/design/data/DesignTableDataManager.java index 468982d96..35a539a50 100644 --- a/designer-base/src/main/java/com/fr/design/data/DesignTableDataManager.java +++ b/designer-base/src/main/java/com/fr/design/data/DesignTableDataManager.java @@ -3,19 +3,22 @@ package com.fr.design.data; import com.fr.base.StoreProcedureParameter; import com.fr.base.TableData; import com.fr.concurrent.NamedThreadFactory; +import com.fr.data.MultiResultTableData; import com.fr.data.TableDataSource; import com.fr.data.TableDataSourceTailor; import com.fr.data.core.DataCoreXmlUtils; import com.fr.data.impl.EmbeddedTableData; +import com.fr.data.impl.NameDataModel; import com.fr.data.impl.storeproc.ProcedureDataModel; import com.fr.data.impl.storeproc.StoreProcedure; import com.fr.data.impl.storeproc.StoreProcedureConstants; +import com.fr.data.impl.storeproc.StoreProcedureHelper; import com.fr.data.operator.DataOperator; import com.fr.design.DesignModelAdapter; import com.fr.design.data.datapane.preview.PreviewTablePane; +import com.fr.design.data.tabledata.wrapper.MultiResultTableDataNameWrapper; +import com.fr.design.data.tabledata.wrapper.MultiResultTableDataWrapper; import com.fr.design.data.tabledata.wrapper.ServerTableDataWrapper; -import com.fr.design.data.tabledata.wrapper.StoreProcedureDataWrapper; -import com.fr.design.data.tabledata.wrapper.StoreProcedureNameWrapper; import com.fr.design.data.tabledata.wrapper.TableDataFactory; import com.fr.design.data.tabledata.wrapper.TableDataWrapper; import com.fr.design.data.tabledata.wrapper.TemplateTableDataWrapper; @@ -188,7 +191,7 @@ public abstract class DesignTableDataManager { public static void addDsChangeListener(ChangeListener l) { JTemplate template = HistoryTemplateListCache.getInstance().getCurrentEditingTemplate(); String key = StringUtils.EMPTY; - if (template != null) { + if (JTemplate.isValid(template)) { key = template.getPath(); } List dsListeners = dsListenersMap.get(key); @@ -321,33 +324,44 @@ public abstract class DesignTableDataManager { public static java.util.Map getAllDataSetIncludingProcedure(java.util.Map resMap) { java.util.LinkedHashMap dsMap = new java.util.LinkedHashMap(); - Iterator> entryIt = resMap.entrySet().iterator(); - while (entryIt.hasNext()) { - String key = entryIt.next().getKey(); + for (Entry entry : resMap.entrySet()) { + String key = entry.getKey(); TableDataWrapper tableDataWrapper = resMap.get(key); - if (tableDataWrapper.getTableData() instanceof StoreProcedure) { - StoreProcedure storeProcedure = (StoreProcedure) tableDataWrapper.getTableData(); - boolean hasSchemaOrResult = false; - StoreProcedureParameter[] parameters = StoreProcedure.getSortPara(storeProcedure.getParameters()); + if (tableDataWrapper.getTableData() instanceof MultiResultTableData) { + MultiResultTableData tableData = (MultiResultTableData) tableDataWrapper.getTableData(); String name = tableDataWrapper.getTableDataName(); - List resultNames = storeProcedure.getResultNames(); - TableDataWrapper tdw = new StoreProcedureNameWrapper(name + "_Table", storeProcedure); - - for (StoreProcedureParameter parameter : parameters) { - if (parameter.getSchema() != StoreProcedureConstants.IN) { - String parameterName = name + "_" + parameter.getName(); - TableDataWrapper newTwd = new StoreProcedureDataWrapper(storeProcedure, name, parameterName, false); - dsMap.put(parameterName, newTwd); - hasSchemaOrResult = true; + List resultNames = tableData.getResultNames(); + TableDataWrapper tdw = new MultiResultTableDataNameWrapper(name + "_Table", tableData); + boolean hasSchemaOrResult = false; + + // 存储过程的特殊处理,还有其它名称 + if (tableData instanceof StoreProcedure) { + StoreProcedure storeProcedure = (StoreProcedure) tableData; + StoreProcedureParameter[] parameters = StoreProcedureHelper.getSortPara(storeProcedure.getParameters()); + + for (StoreProcedureParameter parameter : parameters) { + if (parameter.getSchema() != StoreProcedureConstants.IN) { + String parameterName = name + "_" + parameter.getName(); + TableDataWrapper newTwd = new MultiResultTableDataWrapper(storeProcedure, name, parameterName, false); + dsMap.put(parameterName, newTwd); + hasSchemaOrResult = true; + } } - } + } /*else { + // TODO getDataModelList是空的 + for (String n : tableData.getResultNames()) { + String dmName = name + "_" + n; + dsMap.put(n, new MultiResultTableDataWrapper(tableData, name, dmName, false)); + } + }*/ + if (!resultNames.isEmpty()) { hasSchemaOrResult = true; - for (int i = 0; i < resultNames.size(); i++) { - String parameterName = name + "_" + resultNames.get(i); - TableDataWrapper newTwd = new StoreProcedureDataWrapper(storeProcedure, name, parameterName, false); - dsMap.put(parameterName, newTwd); + for (String resultName : resultNames) { + String dmName = name + "_" + resultName; + TableDataWrapper newTwd = new MultiResultTableDataWrapper(tableData, name, dmName, false); + dsMap.put(dmName, newTwd); } } @@ -429,7 +443,7 @@ public abstract class DesignTableDataManager { if (globalDsCache.containsKey(name)) { resMap.put(name, globalDsCache.get(name)); } else { - TableDataWrapper tdw = new StoreProcedureNameWrapper(name, storeProcedure); + TableDataWrapper tdw = new MultiResultTableDataNameWrapper(name, storeProcedure); resMap.put(name, tdw); globalDsCache.put(name, tdw); } @@ -599,29 +613,35 @@ public abstract class DesignTableDataManager { * 所以用该方法,不会对一个已经计算了的存储过程重复计算.和分页预览时处理机制一样,这样对有多个返回数据集的存储过程来说很有必要 * * @param needLoadingBar 是否需要进度条 - * @param storeProcedure 存储过程 + * @param tableData 存储过程 * @return 数据 */ - public static ProcedureDataModel[] createLazyDataModel(StoreProcedure storeProcedure, boolean needLoadingBar) throws Exception { - ByteArrayOutputStream out = new ByteArrayOutputStream(); - XMLPrintWriter writer = XMLPrintWriter.create(out); - // 把storeProcedure写成xml文件到out - DataCoreXmlUtils.writeXMLStoreProcedure(writer, storeProcedure, null); - if (storeProcedure.getDataModelSize() > 0 && !storeProcedure.isFirstExpand()) { - return storeProcedure.creatLazyDataModel(); - } - ParameterProvider[] inParameters = DataOperator.getInstance().getStoreProcedureParameters(storeProcedure); + public static NameDataModel[] createLazyDataModel(MultiResultTableData tableData, boolean needLoadingBar) throws Exception { Map parameterMap = new HashMap<>(); - if (inParameters.length > 0 && !ComparatorUtils.equals(threadLocal.get(), NO_PARAMETER)) {// 检查Parameter. - - showParaWindow(parameterMap, inParameters); + if (tableData instanceof StoreProcedure) { + StoreProcedure storeProcedure = (StoreProcedure) tableData; + ByteArrayOutputStream out = new ByteArrayOutputStream(); + XMLPrintWriter writer = XMLPrintWriter.create(out); + // 把storeProcedure写成xml文件到out + DataCoreXmlUtils.writeXMLStoreProcedure(writer, storeProcedure, null); + if (storeProcedure.getDataModelList().size() > 0 && !storeProcedure.isFirstExpand()) { + return storeProcedure.getDataModelList().toArray(new ProcedureDataModel[0]); + } + ParameterProvider[] inParameters = DataOperator.getInstance().getStoreProcedureParameters(storeProcedure); + if (inParameters.length > 0 && !ComparatorUtils.equals(threadLocal.get(), NO_PARAMETER)) {// 检查Parameter. + showParaWindow(parameterMap, inParameters); + } + storeProcedure.setFirstExpand(false); } - storeProcedure.setFirstExpand(false); + + // 存储过程有些特殊处理 + // 这个就简单直接获取暂存列表吧 + // TODO 参数处理? if (needLoadingBar) { - StoreProcedureDataWrapper.loadingBar.start(); + MultiResultTableDataWrapper.loadingBar.start(); } - return DataOperator.getInstance().previewProcedureDataModel(storeProcedure, parameterMap, 0); + return DataOperator.getInstance().previewMultiResultTableData(tableData, parameterMap, 0); } private static void showParaWindow(final Map parameterMap, ParameterProvider[] inParameters) { diff --git a/designer-base/src/main/java/com/fr/design/data/datapane/TableDataPaneListPane.java b/designer-base/src/main/java/com/fr/design/data/datapane/TableDataPaneListPane.java index 87e322b26..264a93799 100644 --- a/designer-base/src/main/java/com/fr/design/data/datapane/TableDataPaneListPane.java +++ b/designer-base/src/main/java/com/fr/design/data/datapane/TableDataPaneListPane.java @@ -3,9 +3,8 @@ package com.fr.design.data.datapane; import com.fr.base.TableData; import com.fr.base.TableDataBean; import com.fr.config.RemoteConfigEvent; +import com.fr.data.MultiResultTableData; import com.fr.data.TableDataSource; -import com.fr.data.api.StoreProcedureAssist; -import com.fr.data.impl.storeproc.StoreProcedure; import com.fr.design.data.BasicTableDataUtils; import com.fr.design.data.DesignTableDataManager; import com.fr.design.dialog.FineJOptionPane; @@ -69,10 +68,10 @@ public class TableDataPaneListPane extends JListControlPane implements TableData return; } - if (editingType instanceof StoreProcedure && isIncludeUnderline(tempName)) { + if (editingType instanceof MultiResultTableData && isIncludeUnderline(tempName)) { isNamePermitted = false; nameableList.stopEditing(); - FineJOptionPane.showMessageDialog(SwingUtilities.getWindowAncestor(TableDataPaneListPane.this), Toolkit.i18nText("Fine-Design_Basic_Stored_Procedure_Name_Tips")); + FineJOptionPane.showMessageDialog(SwingUtilities.getWindowAncestor(TableDataPaneListPane.this), Toolkit.i18nText("Fine-Design_Basic_Multi_Result_Table_Data_Name_Tips")); setIllegalIndex(editingIndex); return; } @@ -137,7 +136,7 @@ public class TableDataPaneListPane extends JListControlPane implements TableData } private boolean isIncludeUnderline(String name) { - return name.contains(StoreProcedureAssist.GROUP_MARKER); + return name.contains(MultiResultTableData.GROUP_MARKER); } /** diff --git a/designer-base/src/main/java/com/fr/design/data/datapane/TableDataSourceOP.java b/designer-base/src/main/java/com/fr/design/data/datapane/TableDataSourceOP.java index 33e51100e..61c578453 100644 --- a/designer-base/src/main/java/com/fr/design/data/datapane/TableDataSourceOP.java +++ b/designer-base/src/main/java/com/fr/design/data/datapane/TableDataSourceOP.java @@ -2,20 +2,25 @@ package com.fr.design.data.datapane; import com.fr.base.StoreProcedureParameter; import com.fr.base.TableData; +import com.fr.data.MultiResultTableData; import com.fr.data.TableDataSource; import com.fr.data.impl.storeproc.StoreProcedure; import com.fr.data.impl.storeproc.StoreProcedureConstants; +import com.fr.data.impl.storeproc.StoreProcedureHelper; import com.fr.design.DesignModelAdapter; import com.fr.design.data.DesignTableDataManager; -import com.fr.design.data.tabledata.wrapper.StoreProcedureDataWrapper; -import com.fr.design.data.tabledata.wrapper.StoreProcedureNameWrapper; +import com.fr.design.data.tabledata.wrapper.MultiResultTableDataNameWrapper; +import com.fr.design.data.tabledata.wrapper.MultiResultTableDataWrapper; import com.fr.design.data.tabledata.wrapper.TableDataWrapper; import com.fr.design.gui.itree.refreshabletree.ExpandMutableTreeNode; import com.fr.design.gui.itree.refreshabletree.UserObjectOP; - import com.fr.general.NameObject; -import java.util.*; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; +import java.util.Map; import java.util.Map.Entry; /** @@ -155,9 +160,9 @@ public class TableDataSourceOP implements UserObjectOP { list.add(initStoreProcedureNode(storeProcedureMap)); for (int i = 0; i < getNodeArrayFromMap(storeProcedureMap).length; i++) { ExpandMutableTreeNode tmpNode = getNodeArrayFromMap(storeProcedureMap)[i]; - if (((NameObject) tmpNode.getUserObject()).getObject() instanceof StoreProcedureNameWrapper) { - TableData tableData = ((StoreProcedureNameWrapper) (((NameObject) tmpNode.getUserObject()).getObject())).getStoreProcedure(); - setStoreProcedureTree(tableData, tmpNode); + if (((NameObject) tmpNode.getUserObject()).getObject() instanceof MultiResultTableDataNameWrapper) { + TableData tableData = ((MultiResultTableDataNameWrapper) (((NameObject) tmpNode.getUserObject()).getObject())).getTableData(); + setStoreProcedureTree((MultiResultTableData) tableData, tmpNode); serverlist.add(tmpNode); } } @@ -176,8 +181,8 @@ public class TableDataSourceOP implements UserObjectOP { for (int i = 0; i < getNodeArrayFromMap(dataMap).length; i++) { ExpandMutableTreeNode tmpNode = getNodeArrayFromMap(dataMap)[i]; TableData tableData = ((TableDataWrapper) (((NameObject) tmpNode.getUserObject()).getObject())).getTableData(); - if (tableData instanceof StoreProcedure) { - setStoreProcedureTree(tableData, tmpNode); + if (tableData instanceof MultiResultTableData) { + setStoreProcedureTree((MultiResultTableData) tableData, tmpNode); dataList.add(tmpNode); } else { dataList.add(tmpNode); @@ -185,31 +190,49 @@ public class TableDataSourceOP implements UserObjectOP { } } - protected void setStoreProcedureTree(TableData tableData, ExpandMutableTreeNode tmpNode) { + protected void setStoreProcedureTree(MultiResultTableData tableData, ExpandMutableTreeNode tmpNode) { ArrayList nodeName = new ArrayList<>(); - StoreProcedure storeProcedure = (StoreProcedure) tableData; String name = ((NameObject) tmpNode.getUserObject()).getName(); - StoreProcedureParameter[] parameters = StoreProcedure.getSortPara(storeProcedure.getParameters()); - List resultNames = storeProcedure.getResultNames(); + List resultNames = tableData.getResultNames(); boolean hasChild = false; tmpNode.remove(0); - TableDataWrapper tdw = new StoreProcedureNameWrapper(name + "_Table1", storeProcedure); + TableDataWrapper tdw = new MultiResultTableDataNameWrapper(name + "_Table1", tableData); ExpandMutableTreeNode childNode = new ExpandMutableTreeNode(new NameObject("Table", tdw)); childNode.add(new ExpandMutableTreeNode()); tmpNode.add(childNode); - for (StoreProcedureParameter parameter : parameters) { - if (parameter.getSchema() != StoreProcedureConstants.IN) { - if (!nodeName.contains(parameter.getName())) { - nodeName.add(parameter.getName()); - hasChild = true; - String parameterName = name + "_" + parameter.getName(); - TableDataWrapper newTwd = new StoreProcedureDataWrapper(storeProcedure, name, parameterName, false); - ExpandMutableTreeNode newChildNode = new ExpandMutableTreeNode(new NameObject(parameter.getName(), newTwd)); - newChildNode.add(new ExpandMutableTreeNode()); - tmpNode.add(newChildNode); + + if (tableData instanceof StoreProcedure) { + StoreProcedure storeProcedure = (StoreProcedure) tableData; + StoreProcedureParameter[] parameters = StoreProcedureHelper.getSortPara(storeProcedure.getParameters()); + for (StoreProcedureParameter parameter : parameters) { + if (parameter.getSchema() != StoreProcedureConstants.IN) { + if (!nodeName.contains(parameter.getName())) { + nodeName.add(parameter.getName()); + hasChild = true; + String parameterName = name + "_" + parameter.getName(); + TableDataWrapper newTwd = new MultiResultTableDataWrapper(tableData, name, parameterName, false); + ExpandMutableTreeNode newChildNode = new ExpandMutableTreeNode(new NameObject(parameter.getName(), newTwd)); + newChildNode.add(new ExpandMutableTreeNode()); + tmpNode.add(newChildNode); + } } } - } + } /*else { + if (tableData.getDataModelList().size() > 1) { + for (NameDataModel nameDataModel : tableData.getDataModelList()) { + if (!nodeName.contains(nameDataModel.getName())) { + nodeName.add(nameDataModel.getName()); + hasChild = true; + String parameterName = name + "_" + nameDataModel.getName(); + TableDataWrapper newTwd = new MultiResultTableDataWrapper(tableData, name, parameterName, false); + ExpandMutableTreeNode newChildNode = new ExpandMutableTreeNode(new NameObject(nameDataModel.getName(), newTwd)); + newChildNode.add(new ExpandMutableTreeNode()); + tmpNode.add(newChildNode); + } + } + } + }*/ + if (!resultNames.isEmpty()) { for (String resultName : resultNames) { @@ -217,7 +240,7 @@ public class TableDataSourceOP implements UserObjectOP { nodeName.add(resultName); hasChild = true; String parameterName = name + "_" + resultName; - TableDataWrapper newTwd = new StoreProcedureDataWrapper(storeProcedure, name, parameterName, false); + TableDataWrapper newTwd = new MultiResultTableDataWrapper(tableData, name, parameterName, false); ExpandMutableTreeNode newChildNode = new ExpandMutableTreeNode(new NameObject(resultName, newTwd)); newChildNode.add(new ExpandMutableTreeNode()); tmpNode.add(newChildNode); diff --git a/designer-base/src/main/java/com/fr/design/data/datapane/TableDataTree.java b/designer-base/src/main/java/com/fr/design/data/datapane/TableDataTree.java index 0c000f5af..e8ac19fd0 100644 --- a/designer-base/src/main/java/com/fr/design/data/datapane/TableDataTree.java +++ b/designer-base/src/main/java/com/fr/design/data/datapane/TableDataTree.java @@ -1,7 +1,7 @@ package com.fr.design.data.datapane; import com.fr.base.BaseUtils; -import com.fr.data.impl.storeproc.StoreProcedure; +import com.fr.data.MultiResultTableData; import com.fr.design.constants.UIConstants; import com.fr.design.data.datapane.management.search.TableDataTreeSearchManager; import com.fr.design.data.tabledata.wrapper.AbstractTableDataWrapper; @@ -379,7 +379,7 @@ public class TableDataTree extends UserObjectRefreshJTree { return false; } Object userObject = treeNode.getUserObject(); - if (userObject instanceof NameObject && ((NameObject) userObject).getObject() instanceof AbstractTableDataWrapper) { + if (userObject instanceof NameObject && ((NameObject) userObject).getObject() instanceof TableDataWrapper) { return true; } return false; @@ -462,7 +462,7 @@ public class TableDataTree extends UserObjectRefreshJTree { if (userObject instanceof NameObject) { NameObject nameObject = (NameObject) userObject; TableDataWrapper tableDataWrapper = (TableDataWrapper) nameObject.getObject(); - return tableDataWrapper.getTableData() instanceof StoreProcedure; + return tableDataWrapper.getTableData() instanceof MultiResultTableData; } return false; } diff --git a/designer-base/src/main/java/com/fr/design/data/datapane/TableDataTreePane.java b/designer-base/src/main/java/com/fr/design/data/datapane/TableDataTreePane.java index a7dbce4df..880cccd2d 100644 --- a/designer-base/src/main/java/com/fr/design/data/datapane/TableDataTreePane.java +++ b/designer-base/src/main/java/com/fr/design/data/datapane/TableDataTreePane.java @@ -1,6 +1,7 @@ package com.fr.design.data.datapane; import com.fr.base.TableData; +import com.fr.data.MultiResultTableData; import com.fr.data.TableDataSource; import com.fr.data.impl.DBTableData; import com.fr.data.impl.TableDataSourceDependent; @@ -15,10 +16,10 @@ import com.fr.design.data.DesignTableDataManager; import com.fr.design.data.StrategyConfigAttrUtils; import com.fr.design.data.datapane.auth.TableDataAuthHelper; import com.fr.design.data.datapane.management.clip.TableDataTreeClipboard; +import com.fr.design.data.datapane.management.search.TableDataTreeSearchManager; import com.fr.design.data.datapane.management.search.pane.TableDataSearchRemindPane; import com.fr.design.data.datapane.management.search.pane.TreeSearchToolbarPane; import com.fr.design.data.datapane.management.search.searcher.TableDataSearchMode; -import com.fr.design.data.datapane.management.search.TableDataTreeSearchManager; import com.fr.design.data.tabledata.StoreProcedureWorkerListener; import com.fr.design.data.tabledata.paste.TableDataFollowingPasteUtils; import com.fr.design.data.tabledata.tabledatapane.AbstractTableDataPane; @@ -121,6 +122,7 @@ public class TableDataTreePane extends BasicTableDataTreePane { /** * 获取不必每次都refreshDockingView的数据集树面板 * 不会主动替换DesignModelAdapter,需要保证使用时没有跨模板动作,谨慎使用 + * * @param tc * @return */ @@ -484,7 +486,7 @@ public class TableDataTreePane extends BasicTableDataTreePane { tdNamePanel.addPropertyChangeListener(new PropertyChangeAdapter() { @Override public void propertyChange() { - doPropertyChange(dg, tdNamePanel, oldName); + checkNameChange(tableDataPane, dg, tdNamePanel, oldName); } }); // 有些数据集(DBTableData)面板的初始化过程中是包含了SwingWorker处理(查询数据连接、查表等)的 @@ -494,6 +496,34 @@ public class TableDataTreePane extends BasicTableDataTreePane { }); } + private void checkNameChange(AbstractTableDataPane tableDataPane, BasicDialog dg, BasicPane.NamePane nPanel, final String oldName) { + nPanel.setShowText(StringUtils.BLANK); + dg.setButtonEnabled(true); + String tempName = nPanel.getObjectName(); + if (StringUtils.isBlank(tempName)) { + nPanel.setShowText(com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Basic_Table_Data_Empty_Name_Tips")); + dg.setButtonEnabled(false); + } else if (!ComparatorUtils.equals(oldName, tempName) && isDsNameRepeaded(tempName)) { + nPanel.setShowText(com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Basic_Table_Data_Duplicate_Name_Tips", tempName)); + dg.setButtonEnabled(false); + } else if (oldName.length() >= PROCEDURE_NAME_INDEX && tableDataPane.updateBean() instanceof MultiResultTableData) { + if (isIncludeUnderline(tempName)) { + nPanel.setShowText(com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Basic_Multi_Result_Table_Data_Name_Tips")); + dg.setButtonEnabled(false); + } + } else if (!BasicTableDataUtils.checkName(tempName)) { + dg.setButtonEnabled(false); + } else { + nPanel.setShowText(StringUtils.BLANK); + dg.setButtonEnabled(true); + } + } + + + private boolean isIncludeUnderline(String name) { + return !ComparatorUtils.equals(name.indexOf(MultiResultTableData.GROUP_MARKER), -1); + } + @Override public void removeTableData(String sourceName) { TableDataSource tds = this.tc.getBook(); 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..5dd7dca5f 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,9 @@ 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.data.tabledata.wrapper.MultiResultTableDataWrapper; +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; @@ -39,7 +40,7 @@ public class TableDataSearchTask implements TreeSearchTask { TreeSearchResult result; try { if (isTableDataStoreProcedure(tableDataWrapper)) { - result = dealWithStoreProcedureTableDataWrapper((StoreProcedureDataWrapper) tableDataWrapper); + result = dealWithStoreProcedureTableDataWrapper((MultiResultTableDataWrapper) tableDataWrapper); } else { result = dealWithCommonTableDataWrapper(tableDataWrapper); } @@ -87,11 +88,11 @@ public class TableDataSearchTask implements TreeSearchTask { * * @param procedureDataWrapper */ - private TreeSearchResult dealWithStoreProcedureTableDataWrapper(StoreProcedureDataWrapper procedureDataWrapper) { + private TreeSearchResult dealWithStoreProcedureTableDataWrapper(MultiResultTableDataWrapper procedureDataWrapper) { // 存储过程数据集名称,例如 Proc1_Table1 String tableDataName = procedureDataWrapper.getTableDataName(); // 存储过程名称,例如 Proc1 - String storeProcedureName = procedureDataWrapper.getStoreprocedureName(); + String storeProcedureName = procedureDataWrapper.getTableDataName(); // 存储过程子表名称,例如 Table1 String tableName = tableDataName.replaceFirst(storeProcedureName, StringUtils.EMPTY).replaceFirst("_", StringUtils.EMPTY); boolean isStoreProcedureNameMatch = isMatchSearch(storeProcedureName, searchText); @@ -132,7 +133,7 @@ public class TableDataSearchTask implements TreeSearchTask { * @return */ private boolean isTableDataStoreProcedure(TableDataWrapper tableDataWrapper) { - return tableDataWrapper instanceof StoreProcedureDataWrapper; + return tableDataWrapper instanceof MultiResultTableDataWrapper; } /** 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/data/datapane/preview/PreviewTableModel.java b/designer-base/src/main/java/com/fr/design/data/datapane/preview/PreviewTableModel.java index 46706cf59..abbe3a7e0 100644 --- a/designer-base/src/main/java/com/fr/design/data/datapane/preview/PreviewTableModel.java +++ b/designer-base/src/main/java/com/fr/design/data/datapane/preview/PreviewTableModel.java @@ -3,7 +3,7 @@ package com.fr.design.data.datapane.preview; import com.fr.cache.list.IntList; import com.fr.data.AbstractDataModel; import com.fr.data.impl.EmbeddedTableData.EmbeddedTDDataModel; -import com.fr.data.impl.storeproc.ProcedureDataModel; +import com.fr.data.impl.NameDataModel; import com.fr.design.utils.DesignUtils; import com.fr.general.data.DataModel; import com.fr.general.data.TableDataException; @@ -32,8 +32,9 @@ public class PreviewTableModel extends AbstractTableModel { } public PreviewTableModel(DataModel sourceResultSet, int maxRowCount) { - if (sourceResultSet instanceof ProcedureDataModel) { - ProcedureDataModel rs = (ProcedureDataModel) sourceResultSet; + // 如果是这种NameDataModel,根据maxRowCount截断一下 + if (sourceResultSet instanceof NameDataModel) { + NameDataModel rs = (NameDataModel) sourceResultSet; try { this.dataModel = createRowDataModel(rs, maxRowCount); } catch (TableDataException e) { @@ -45,7 +46,7 @@ public class PreviewTableModel extends AbstractTableModel { } } - public static DataModel createRowDataModel(final ProcedureDataModel rs, int maxRowCount) throws TableDataException { + public static DataModel createRowDataModel(final NameDataModel rs, int maxRowCount) throws TableDataException { int rowCount = rs.getRowCount(); if (maxRowCount == 0) { maxRowCount = rowCount; diff --git a/designer-base/src/main/java/com/fr/design/data/datapane/preview/PreviewTablePane.java b/designer-base/src/main/java/com/fr/design/data/datapane/preview/PreviewTablePane.java index 409ae7f7f..5926dcb2d 100644 --- a/designer-base/src/main/java/com/fr/design/data/datapane/preview/PreviewTablePane.java +++ b/designer-base/src/main/java/com/fr/design/data/datapane/preview/PreviewTablePane.java @@ -8,7 +8,7 @@ import com.fr.base.TableData; import com.fr.data.TableDataSource; import com.fr.data.impl.DBTableData; import com.fr.data.impl.EmbeddedTableData; -import com.fr.data.impl.storeproc.ProcedureDataModel; +import com.fr.data.impl.NameDataModel; import com.fr.data.operator.DataOperator; import com.fr.design.DesignerEnvManager; import com.fr.design.data.DesignTableDataManager; @@ -28,6 +28,7 @@ import com.fr.design.mainframe.DesignerContext; import com.fr.design.ui.util.UIUtil; import com.fr.function.TIME; import com.fr.general.FRFont; +import com.fr.general.data.DataModel; import com.fr.log.FineLoggerFactory; import javax.swing.BorderFactory; @@ -55,6 +56,7 @@ import java.awt.event.MouseEvent; import java.sql.Timestamp; import java.util.ArrayList; import java.util.Date; +import java.util.List; import java.util.concurrent.CancellationException; /** @@ -62,13 +64,13 @@ import java.util.concurrent.CancellationException; */ public class PreviewTablePane extends BasicPane { private TableData tableData; - private ProcedureDataModel storeProcedureDataModel; - private static UINumberField maxPreviewNumberField; + private DataModel dataModel; + private UINumberField maxPreviewNumberField; private UINumberField currentRowsField; private JTable preveiwTable; private static AutoProgressBar progressBar; private AutoProgressBar connectionBar; - private java.util.List listeners = new ArrayList(); + private List listeners = new ArrayList(); private BasicDialog dialog; private SwingWorker worker; @@ -152,8 +154,8 @@ public class PreviewTablePane extends BasicPane { refreshLabel.setBackground(java.awt.Color.WHITE); try { populate(tableData); - if (storeProcedureDataModel != null) { - populateStoreDataSQL(); + if (dataModel != null) { + setRowsLimitTableModel(); } } catch (Exception e1) { } @@ -423,7 +425,8 @@ public class PreviewTablePane extends BasicPane { * * @param storeProcedureDataModel storeProcedureDataModel */ - public static void previewStoreData(ProcedureDataModel storeProcedureDataModel) { + @Deprecated + public static void previewStoreData(NameDataModel storeProcedureDataModel) { previewStoreData(storeProcedureDataModel, -1, -1); } @@ -434,13 +437,30 @@ public class PreviewTablePane extends BasicPane { * @param keyIndex 实际值 * @param valueIndex 显示值 */ - public static void previewStoreData(final ProcedureDataModel storeProcedureDataModel, final int keyIndex, final int valueIndex) { + @Deprecated + public static void previewStoreData(final NameDataModel storeProcedureDataModel, final int keyIndex, final int valueIndex) { + previewDataModel(storeProcedureDataModel, keyIndex, valueIndex); + } + + public static void previewDataModel(DataModel dataModel) { + previewDataModel(dataModel, -1, -1); + } + + + /** + * 直接预览数据集的结果集 + * + * @param dataModel 结果集 + * @param keyIndex + * @param valueIndex + */ + public static void previewDataModel(final DataModel dataModel, final int keyIndex, final int valueIndex) { final PreviewTablePane previewTablePane = new PreviewTablePane(); - previewTablePane.storeProcedureDataModel = storeProcedureDataModel; + previewTablePane.dataModel = dataModel; previewTablePane.setBorder(BorderFactory.createTitledBorder(Toolkit.i18nText("Fine-Design_Basic_Data"))); try { - previewTablePane.populateStoreDataSQL(); + previewTablePane.setRowsLimitTableModel(); previewTablePane.resetPreviewTableColumnColor(); if (keyIndex > -1) { @@ -457,27 +477,28 @@ public class PreviewTablePane extends BasicPane { previewTablePane.showWindow(new JFrame()).setVisible(true); } - /** - * 直接预览存储过程的所有返回数据集,没有实际值和显示值 - * - * @param storeProcedureDataModels storeProcedureDataModels - */ - public static void previewStoreDataWithAllDs(ProcedureDataModel[] storeProcedureDataModels) { - UITabbedPane tabPreviewpane = new UITabbedPane(); - int tableSize = storeProcedureDataModels.length; - for (int i = 0; i < tableSize; i++) { + + public static void previewMultiDataModels(NameDataModel[] nameDataModels) { + // tab窗口 + UITabbedPane tabbedPane = new UITabbedPane(); + + for (NameDataModel nameDataModel : nameDataModels) { + // 单个结果集的展示面板 PreviewTablePane previewTablePane = new PreviewTablePane(); - previewTablePane.storeProcedureDataModel = storeProcedureDataModels[i]; + previewTablePane.dataModel = nameDataModel; + // 数据 previewTablePane.setBorder(BorderFactory.createTitledBorder(Toolkit.i18nText("Fine-Design_Basic_Data"))); try { - previewTablePane.populateStoreDataSQL(); + // 带行数限制的数据集结果预览对象 + previewTablePane.setRowsLimitTableModel(); } catch (Exception e) { FineLoggerFactory.getLogger().error(e.getMessage(), e); } - tabPreviewpane.addTab(storeProcedureDataModels[i].getName(), previewTablePane); + tabbedPane.addTab(nameDataModel.getName(), previewTablePane); } - BasicPane prieviewPane = new BasicPane() { + // 包含整个tab的容器窗口 + BasicPane previewPane = new BasicPane() { @Override protected String title4PopupWindow() { @@ -485,15 +506,25 @@ public class PreviewTablePane extends BasicPane { } }; - prieviewPane.setLayout(FRGUIPaneFactory.createBorderLayout()); - prieviewPane.add(tabPreviewpane, BorderLayout.CENTER); - prieviewPane.showWindow(new JFrame()).setVisible(true); + previewPane.setLayout(FRGUIPaneFactory.createBorderLayout()); + previewPane.add(tabbedPane, BorderLayout.CENTER); + previewPane.showWindow(new JFrame()).setVisible(true); + } + + /** + * 直接预览存储过程的所有返回数据集,没有实际值和显示值 + * + * @param storeProcedureDataModels storeProcedureDataModels + */ + @Deprecated + public static void previewStoreDataWithAllDs(NameDataModel[] storeProcedureDataModels) { + previewMultiDataModels(storeProcedureDataModels); } - private void populateStoreDataSQL() throws Exception { + private void setRowsLimitTableModel() throws Exception { PreviewTableModel previewModel; try { - previewModel = new PreviewTableModel(storeProcedureDataModel, (int) maxPreviewNumberField.getValue()); + previewModel = new PreviewTableModel(dataModel, (int) maxPreviewNumberField.getValue()); } catch (Exception e) { previewModel = new PreviewTableModel((int) maxPreviewNumberField.getValue()); } diff --git a/designer-base/src/main/java/com/fr/design/data/tabledata/tabledatapane/ProcedureDataPane.java b/designer-base/src/main/java/com/fr/design/data/tabledata/tabledatapane/ProcedureDataPane.java index a2a279b60..b57ca864f 100644 --- a/designer-base/src/main/java/com/fr/design/data/tabledata/tabledatapane/ProcedureDataPane.java +++ b/designer-base/src/main/java/com/fr/design/data/tabledata/tabledatapane/ProcedureDataPane.java @@ -19,7 +19,7 @@ import com.fr.design.data.datapane.connect.ConnectionTableProcedurePane.DoubleCl import com.fr.design.data.datapane.sqlpane.SQLEditPane; import com.fr.design.data.tabledata.ResponseDataSourceChange; import com.fr.design.data.tabledata.StoreProcedureWorkerListener; -import com.fr.design.data.tabledata.wrapper.StoreProcedureDataWrapper; +import com.fr.design.data.tabledata.wrapper.MultiResultTableDataWrapper; import com.fr.design.dialog.FineJOptionPane; import com.fr.design.gui.icheckbox.UICheckBox; import com.fr.design.gui.icontainer.UIScrollPane; @@ -289,16 +289,16 @@ public class ProcedureDataPane extends AbstractTableDataPane imp @Override protected Void doInBackground() throws Exception { DesignTableDataManager.setThreadLocal(DesignTableDataManager.NO_PARAMETER); - sp.setCalculating(true); - ProcedureDataModel[] dataModels = DesignTableDataManager.createLazyDataModel(sp, false); - sp.refreshDataModelListAndResultNames(dataModels); + // sp.setCalculating(true); + ProcedureDataModel[] dataModels = (ProcedureDataModel[]) DesignTableDataManager.createLazyDataModel(sp, false); + //sp.refreshDataModelListAndResultNames(dataModels); return null; } @Override public void done() { DesignTableDataManager.setThreadLocal(DesignTableDataManager.NO_PARAMETER); - sp.setCalculating(false); + // sp.setCalculating(false); doAfterProcudureDone(); fireDSChanged(); TableDataTreePane.getInstance(DesignModelAdapter.getCurrentModelAdapter()); @@ -382,8 +382,8 @@ public class ProcedureDataPane extends AbstractTableDataPane imp @Override public void actionPerformed(ActionEvent evt) { StoreProcedure sp = updateBeanWithOutExecute(); - StoreProcedureDataWrapper storeProcedureDataWrapper = new StoreProcedureDataWrapper(this.procedureDataPane, sp, StringUtils.EMPTY, queryText.getText()); - storeProcedureDataWrapper.previewData(StoreProcedureDataWrapper.PREVIEW_ALL); + MultiResultTableDataWrapper wrapper = new MultiResultTableDataWrapper(this.procedureDataPane, sp, StringUtils.EMPTY, queryText.getText()); + wrapper.previewData(MultiResultTableDataWrapper.PREVIEW_ALL); } } diff --git a/designer-base/src/main/java/com/fr/design/data/tabledata/wrapper/MultiResultTableDataNameWrapper.java b/designer-base/src/main/java/com/fr/design/data/tabledata/wrapper/MultiResultTableDataNameWrapper.java new file mode 100644 index 000000000..610aac3c5 --- /dev/null +++ b/designer-base/src/main/java/com/fr/design/data/tabledata/wrapper/MultiResultTableDataNameWrapper.java @@ -0,0 +1,151 @@ +package com.fr.design.data.tabledata.wrapper; + +import com.fr.base.TableData; +import com.fr.data.MultiResultTableData; +import com.fr.data.impl.NameDataModel; +import com.fr.design.data.DesignTableDataManager; +import com.fr.design.data.datapane.TableDataCreatorProducer; +import com.fr.design.data.datapane.TableDataNameObjectCreator; +import com.fr.design.data.datapane.preview.PreviewTablePane; +import com.fr.design.gui.itree.refreshabletree.ExpandMutableTreeNode; +import com.fr.general.IOUtils; +import com.fr.log.FineLoggerFactory; +import com.fr.workspace.WorkContext; + +import javax.swing.Icon; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + + +/** + * 多结果数据集-结果包装 + * + * @author rinoux + * @version 11.0 + * Created by rinoux on 2022/8/12 + */ +public final class MultiResultTableDataNameWrapper implements TableDataWrapper { + private NameDataModel dataModel; + private final String name; + private final MultiResultTableData tableData; + private List childrenList; + + /** + * @param name 数据集名字 + * @param tableData 数据集 + */ + public MultiResultTableDataNameWrapper(String name, MultiResultTableData tableData) { + this.name = name; + this.tableData = tableData; + } + + /** + * 生成子节点 + * + * @return 子节点 + */ + public ExpandMutableTreeNode[] load() { + // 生成多数据集结果子节点 + List namelist = calculateColumnNameList(); + ExpandMutableTreeNode[] res = new ExpandMutableTreeNode[namelist.size()]; + for (int i = 0; i < res.length; i++) { + res[i] = new ExpandMutableTreeNode(namelist.get(i)); + } + + return res; + } + + @Override + public String getTableDataName() { + return name; + } + + @Override + public TableData getTableData() { + return tableData; + } + + @Override + public Icon getIcon() { + for (TableDataNameObjectCreator creator : TableDataCreatorProducer.getInstance().createReportTableDataCreator()) { + if (creator.createObject().getClass().isAssignableFrom(this.tableData.getClass())) { + return IOUtils.readIcon(creator.getIconPath()); + } + } + return IOUtils.readIcon("/com/fr/design/images/data/multi.png"); + } + + private void createResult(boolean needLoadingBar) { + try { + // todo 啥意思? + dataModel = DesignTableDataManager.createLazyDataModel(tableData, needLoadingBar)[0]; + } catch (Exception e) { + FineLoggerFactory.getLogger().error(e.getMessage(), e); + } + } + + /** + * 数据集执行结果返回的所有字段 + *

+ * TODO:要不要加上Exception呢?个人感觉很有必要 + * + * @return 字段 + */ + public List calculateColumnNameList() { + if (childrenList != null) { + return childrenList; + } + childrenList = new ArrayList<>(); + if (!WorkContext.getCurrent().isLocal()) { + try { + createResult(false); + childrenList = Arrays.asList(dataModel.getColumnNames()); + } catch (Exception e) { + FineLoggerFactory.getLogger().error(e.getMessage(), e); + } + + } else { + if (dataModel == null) { + createResult(false); + } + if (dataModel != null) { + childrenList = Arrays.asList(dataModel.getColumnNames()); + } + } + return childrenList; + } + + /** + * 预览数据集 + */ + public void previewData() { + if (dataModel == null) { + createResult(true); + } + PreviewTablePane.previewDataModel(dataModel); + + } + + /** + * 预览数据集,带有显示值和实际值的标记结果 + * + * @param keyIndex 显示值Index + * @param valueIndex 实际值index + */ + public void previewData(int keyIndex, int valueIndex) { + if (dataModel == null) { + createResult(true); + } + PreviewTablePane.previewDataModel(dataModel, keyIndex, valueIndex); + } + + /** + * 是否异常 + * + * @return 异常返回true + */ + public boolean isUnusual() { + return false; + } +} \ No newline at end of file diff --git a/designer-base/src/main/java/com/fr/design/data/tabledata/wrapper/MultiResultTableDataWrapper.java b/designer-base/src/main/java/com/fr/design/data/tabledata/wrapper/MultiResultTableDataWrapper.java new file mode 100644 index 000000000..6cce86fd2 --- /dev/null +++ b/designer-base/src/main/java/com/fr/design/data/tabledata/wrapper/MultiResultTableDataWrapper.java @@ -0,0 +1,321 @@ +package com.fr.design.data.tabledata.wrapper; + +import com.fr.base.TableData; +import com.fr.data.MultiResultTableData; +import com.fr.data.impl.NameDataModel; +import com.fr.data.impl.storeproc.StoreProcedure; +import com.fr.data.operator.DataOperator; +import com.fr.design.data.DesignTableDataManager; +import com.fr.design.data.datapane.TableDataCreatorProducer; +import com.fr.design.data.datapane.TableDataNameObjectCreator; +import com.fr.design.data.datapane.preview.PreviewTablePane; +import com.fr.design.dialog.FineJOptionPane; +import com.fr.design.gui.iprogressbar.AutoProgressBar; +import com.fr.design.gui.itree.refreshabletree.ExpandMutableTreeNode; +import com.fr.design.i18n.Toolkit; +import com.fr.design.mainframe.DesignerContext; +import com.fr.general.ComparatorUtils; +import com.fr.general.IOUtils; +import com.fr.log.FineLoggerFactory; + +import javax.swing.Icon; +import javax.swing.JFrame; +import javax.swing.SwingWorker; +import java.awt.Component; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.concurrent.CancellationException; + +/** + * 多结果数据集包装 + * + * @author rinoux + * @version 11.0 + * Created by rinoux on 2022/8/12 + */ +public final class MultiResultTableDataWrapper implements TableDataWrapper { + public static final int PREVIEW_ALL = 0; + public static final int PREVIEW_ONE = 1; + public static AutoProgressBar loadingBar; + + private NameDataModel dataModel; + private final String dataModelName; + private final String tableDataName; + private final MultiResultTableData tableData; + private List columnNameList; + private AutoProgressBar connectionBar; + private NameDataModel[] dataModels; + private SwingWorker worker; + private int previewModel; + + /** + * @param tableData 数据集 + * @param tableDataName 数据集名称 + * @param dataModelName 数据集的一个结果的名称(全限定名称) + */ + public MultiResultTableDataWrapper(MultiResultTableData tableData, String tableDataName, String dataModelName) { + this(null, tableData, tableDataName, dataModelName, true); + } + + /** + * @param tableData 数据集 + * @param tableDataName 数据集名称 + * @param dataModelName 数据集的一个结果的名称(全限定名称) + * @param needLoad 是否需要加载 + */ + public MultiResultTableDataWrapper(MultiResultTableData tableData, String tableDataName, String dataModelName, boolean needLoad) { + this(null, tableData, tableDataName, dataModelName, needLoad); + } + + /** + * @param component 父容器 + * @param tableData 数据集 + * @param tableDataName 数据集名称 + * @param dataModelName 数据集的一个结果的名称(全限定名称) + */ + public MultiResultTableDataWrapper(Component component, MultiResultTableData tableData, String tableDataName, String dataModelName) { + this(component, tableData, tableDataName, dataModelName, true); + } + + /** + * @param component loadingBar的父弹框(如果不设置父弹框的话,可能出现loadingBar隐藏在一个弹框后的情况) + * @param tableData 多结果数据集 + * @param tableDataName 多结果数据集的名字(某些情况下可以为空) + * @param dataModelName 多结果数据集一个返回数据集的名字 + * @param needLoad 是否要加载 + **/ + public MultiResultTableDataWrapper(Component component, MultiResultTableData tableData, String tableDataName, String dataModelName, boolean needLoad) { + this.dataModelName = dataModelName; + this.tableData = tableData; + this.tableDataName = tableDataName; + if (component == null) { + component = new JFrame(); + } + if (needLoad) { + setWorker(component); + } + loadingBar = new AutoProgressBar(component, Toolkit.i18nText("Fine-Design_Basic_Loading_Data"), "", 0, 100) { + @Override + public void doMonitorCanceled() { + getWorker().cancel(true); + } + }; + } + + /** + * 数据集执行结果返回的所有字段 + * + * @return 数据集执行结果返回的所有字段 + * @date 2014-12-3-下午7:43:17 + */ + @Override + public List calculateColumnNameList() { + if (columnNameList != null) { + return columnNameList; + } + + try { + createResults(false); + } catch (Exception e) { + FineJOptionPane.showMessageDialog(DesignerContext.getDesignerFrame(), Toolkit.i18nText("Fine-Design_Basic_Engine_No_TableData")); + return new ArrayList(); + } + columnNameList = Arrays.asList(dataModel.getColumnNames()); + return columnNameList; + } + + /** + * 生成子节点 + * + * @return 节点数组 + * @date 2014-12-3-下午7:06:47 + */ + @Override + public ExpandMutableTreeNode[] load() { + List namelist; + if (tableData.isCalculating()) { + namelist = Arrays.asList(new String[0]); + } else { + namelist = calculateColumnNameList(); + } + ExpandMutableTreeNode[] res = new ExpandMutableTreeNode[namelist.size()]; + for (int i = 0; i < res.length; i++) { + res[i] = new ExpandMutableTreeNode(namelist.get(i)); + } + + return res; + } + + private void createResults(boolean needLoadingBar) throws Exception { + + this.dataModel = null; + dataModels = DesignTableDataManager.createLazyDataModel(tableData, needLoadingBar); + if (dataModels != null && dataModels.length != 0) { + for (NameDataModel dataModel : dataModels) { + if (ComparatorUtils.equals(this.dataModelName, tableDataName + MultiResultTableData.GROUP_MARKER + dataModel.getName())) { + this.dataModel = dataModel; + break; + } + } + } + } + + @Override + public Icon getIcon() { + for (TableDataNameObjectCreator creator : TableDataCreatorProducer.getInstance().createReportTableDataCreator()) { + if (creator.createObject().getClass().isAssignableFrom(this.tableData.getClass())) { + return IOUtils.readIcon(creator.getIconPath()); + } + } + return IOUtils.readIcon("/com/fr/design/images/data/multi.png"); + } + + /** + * 预览数据 + * + * @param previewModel 预览模式, 全部还是一个 + * @date 2014-12-3-下午7:05:50 + */ + public void previewData(final int previewModel) { + this.previewModel = previewModel; + connectionBar = new AutoProgressBar(new JFrame(), Toolkit.i18nText("Fine-Design_Basic_Utils_Now_Create_Connection"), "", 0, 100) { + @Override + public void doMonitorCanceled() { + connectionBar.close(); + worker.cancel(true); + } + }; + worker.execute(); + } + + private void setWorker(final Component parent) { + worker = new SwingWorker() { + @Override + protected Void doInBackground() throws Exception { + loadingBar.close(); + PreviewTablePane.resetPreviewTable(); + connectionBar.start(); + + // 存储过程需要先测试一下连接 + if (tableData instanceof StoreProcedure) { + boolean status = DataOperator.getInstance().testConnection(((StoreProcedure) getTableData()).getDatabaseConnection()); + if (!status) { + connectionBar.close(); + throw new Exception(Toolkit.i18nText("Fine-Design_Basic_Database_Connection_Failed")); + } + } + + connectionBar.close(); + tableData.resetDataModelList(); + + // 获取结果 + createResults(true); + return null; + } + + @Override + public void done() { + try { + get(); + loadingBar.close(); + switch (previewModel) { + case MultiResultTableDataWrapper.PREVIEW_ALL: + PreviewTablePane.previewMultiDataModels(dataModels); + break; + case MultiResultTableDataWrapper.PREVIEW_ONE: + previewData(); + break; + default: + break; + } + } catch (Exception e) { + loadingBar.close(); + if (!(e instanceof CancellationException)) { + FineLoggerFactory.getLogger().error(e.getMessage(), e); + FineJOptionPane.showMessageDialog(parent, e.getMessage()); + } + } + } + }; + } + + private SwingWorker getWorker() { + return this.worker; + } + + // august:这个只是预览返回的一个数据集 + + /** + * 预览返回的一个数据集 + * + * @date 2014-12-3-下午7:42:53 + */ + @Override + public void previewData() { + previewData(-1, -1); + } + + // august:这个只是预览返回的一个数据集 + + /** + * 预览返回的一个数据集,带有显示值和实际值的标记结果 + * + * @param keyIndex 实际值 + * @param valueIndex 显示值 + * @date 2014-12-3-下午7:42:27 + */ + @Override + public void previewData(final int keyIndex, final int valueIndex) { + PreviewTablePane.previewDataModel(dataModel, keyIndex, valueIndex); + } + + + /** + * 预览返回的所有数据集,只有在编辑多结果数据集时才用到 + */ + public void previewAllTable() { + if (dataModel == null) { + try { + createResults(true); + } catch (Exception e) { + return; + } + } + PreviewTablePane.previewMultiDataModels(dataModels); + } + + @Override + public String getTableDataName() { + return tableDataName; + } + + public String getDataModelName() { + return dataModelName; + } + + @Override + public TableData getTableData() { + return tableData; + } + + /** + * 是否异常 + * + * @return 是否异常 + */ + @Override + public boolean isUnusual() { + return false; + } + + @Override + public boolean equals(Object obj) { + return obj instanceof MultiResultTableDataWrapper + && ComparatorUtils.equals(this.dataModelName, ((MultiResultTableDataWrapper) obj).getTableDataName()) + && ComparatorUtils.equals(this.tableData, ((MultiResultTableDataWrapper) obj).getTableData()) + && ComparatorUtils.equals(this.tableDataName, ((MultiResultTableDataWrapper) obj).getTableDataName()); + + } + +} diff --git a/designer-base/src/main/java/com/fr/design/data/tabledata/wrapper/StoreProcedureDataWrapper.java b/designer-base/src/main/java/com/fr/design/data/tabledata/wrapper/StoreProcedureDataWrapper.java index 515bd8dd0..9eb09376e 100644 --- a/designer-base/src/main/java/com/fr/design/data/tabledata/wrapper/StoreProcedureDataWrapper.java +++ b/designer-base/src/main/java/com/fr/design/data/tabledata/wrapper/StoreProcedureDataWrapper.java @@ -36,7 +36,9 @@ import java.util.concurrent.CancellationException; * * @author zhou * @since 2012-4-12上午10:29:15 + * @deprecated 请勿使用,见{@link MultiResultTableDataWrapper} */ +@Deprecated public final class StoreProcedureDataWrapper implements TableDataWrapper { public static final int PREVIEW_ALL = 0; public static final int PREVIEW_ONE = 1; @@ -74,7 +76,7 @@ public final class StoreProcedureDataWrapper implements TableDataWrapper { public StoreProcedureDataWrapper(Component component, StoreProcedure storeProcedure, String storeprocedureName, String dsName, boolean needLoad) { this.dsName = dsName; this.storeProcedure = storeProcedure; - this.storeProcedure.setCalculating(false); + /*this.storeProcedure.setCalculating(false);*/ this.storeprocedureName = storeprocedureName; if (component == null) { component = new JFrame(); @@ -135,7 +137,7 @@ public final class StoreProcedureDataWrapper implements TableDataWrapper { } private void createStore(boolean needLoadingBar) throws Exception { - dataModels = DesignTableDataManager.createLazyDataModel(storeProcedure, needLoadingBar); + dataModels = (ProcedureDataModel[]) DesignTableDataManager.createLazyDataModel(storeProcedure, needLoadingBar); if (dataModels != null && dataModels.length != 0) { for (ProcedureDataModel dataModel : dataModels) { if (ComparatorUtils.equals(this.dsName, storeprocedureName + "_" + dataModel.getName())) { diff --git a/designer-base/src/main/java/com/fr/design/data/tabledata/wrapper/StoreProcedureNameWrapper.java b/designer-base/src/main/java/com/fr/design/data/tabledata/wrapper/StoreProcedureNameWrapper.java index a0d0182ac..11aeedbab 100644 --- a/designer-base/src/main/java/com/fr/design/data/tabledata/wrapper/StoreProcedureNameWrapper.java +++ b/designer-base/src/main/java/com/fr/design/data/tabledata/wrapper/StoreProcedureNameWrapper.java @@ -27,7 +27,9 @@ import java.util.List; * * @author zhou * @since 2012-4-12上午10:29:15 + * @deprecated 请勿使用,见{@link MultiResultTableDataNameWrapper} */ +@Deprecated public final class StoreProcedureNameWrapper implements TableDataWrapper { private ProcedureDataModel procedureDataModel; private String name; @@ -74,7 +76,7 @@ public final class StoreProcedureNameWrapper implements TableDataWrapper { private void createStore(boolean needLoadingBar) { try { - procedureDataModel = DesignTableDataManager.createLazyDataModel(storeProcedure, needLoadingBar)[0]; + procedureDataModel = (ProcedureDataModel) DesignTableDataManager.createLazyDataModel(storeProcedure, needLoadingBar)[0]; } catch (Exception e) { FineLoggerFactory.getLogger().error(e.getMessage(), e); } diff --git a/designer-base/src/main/java/com/fr/design/deeplink/DeepLinkCore.java b/designer-base/src/main/java/com/fr/design/deeplink/DeepLinkCore.java index 55a45709b..c9b80ebf7 100644 --- a/designer-base/src/main/java/com/fr/design/deeplink/DeepLinkCore.java +++ b/designer-base/src/main/java/com/fr/design/deeplink/DeepLinkCore.java @@ -10,6 +10,7 @@ import com.fr.event.Null; import com.fr.log.FineLoggerFactory; import com.fr.stable.StringUtils; import com.fr.stable.os.OperatingSystem; +import com.fr.start.common.DesignerStartupContext; import com.fr.third.org.apache.http.NameValuePair; import com.fr.web.URLUtils; @@ -42,6 +43,8 @@ public class DeepLinkCore { } private String pendingURL; + + private final List deepLinkPrepareList = new ArrayList<>(); private final List deepLinkList = new ArrayList<>(); @@ -50,6 +53,9 @@ public class DeepLinkCore { public void register(DeepLink deepLink) { if (deepLink != null) { deepLinkList.add(deepLink); + if (deepLink instanceof DeepLinkPrepare) { + deepLinkPrepareList.add((DeepLinkPrepare) deepLink); + } } } @@ -128,39 +134,35 @@ public class DeepLinkCore { private void acceptNewURL(String url) { this.pendingURL = url; + + UrlBean urlBean = UrlBean.create(this.pendingURL); + for (DeepLinkPrepare deepLinkPrepare : deepLinkPrepareList) { + deepLinkPrepare.prepare(urlBean.getUrl(), urlBean.getHost(), urlBean.getPath(), urlBean.getParams()); + } } private boolean canConsumePendingURL() { - return StringUtils.isNotEmpty(this.pendingURL) && isDesignerStartupCompleted; + return StringUtils.isNotEmpty(this.pendingURL) && isAvailableConsumingTime(); + } + + /** + * 是否是可用的消耗时机 + * 满足任一即可 + * 1-设计器已经启动 + * 2-出在设计器启动页页面 + * + * @return 是/否 + */ + private boolean isAvailableConsumingTime() { + return isDesignerStartupCompleted || DesignerStartupContext.getInstance().isOnWaiting(); } private void consumePendingURL() { - String host = null; - String path = null; - Map params = new HashMap<>(); - - URL url = null; - try { - url = new URL(null, this.pendingURL, new URLStreamHandler() { - @Override - protected URLConnection openConnection(URL u) throws IOException { - return null; - } - }); - } catch (MalformedURLException ignored) {} - - if (url != null) { - host = url.getHost(); - path = url.getPath(); - - List pairs = URLUtils.parse(url.getQuery()); - for (NameValuePair pair: pairs) { - params.put(pair.getName(), pair.getValue()); - } - } - + + UrlBean urlBean = UrlBean.create(this.pendingURL); + FineLoggerFactory.getLogger().info("consume deep link: " + this.pendingURL); - performDeepLinks(this.pendingURL, host, path, params); + performDeepLinks(urlBean.getUrl(), urlBean.getHost(), urlBean.getPath(), urlBean.getParams()); markPendingURLConsumed(); } @@ -181,4 +183,67 @@ public class DeepLinkCore { private void markPendingURLConsumed() { this.pendingURL = null; } + + private static class UrlBean { + + private String url; + + private String host; + + private String path; + + private Map params; + + public UrlBean(String url, String host, String path, Map params) { + this.url = url; + this.host = host; + this.path = path; + this.params = params; + } + + public String getUrl() { + return url; + } + + public String getHost() { + return host; + } + + public String getPath() { + return path; + } + + public Map getParams() { + return params; + } + + public static UrlBean create(String pendingUrl) { + + String host = null; + String path = null; + Map params = new HashMap<>(); + + URL url = null; + try { + url = new URL(null, pendingUrl, new URLStreamHandler() { + @Override + protected URLConnection openConnection(URL u) throws IOException { + return null; + } + }); + } catch (MalformedURLException ignored) { + } + + if (url != null) { + host = url.getHost(); + path = url.getPath(); + + List pairs = URLUtils.parse(url.getQuery()); + for (NameValuePair pair : pairs) { + params.put(pair.getName(), pair.getValue()); + } + } + return new UrlBean(pendingUrl, host, path, params); + } + } } diff --git a/designer-base/src/main/java/com/fr/design/deeplink/DeepLinkPrepare.java b/designer-base/src/main/java/com/fr/design/deeplink/DeepLinkPrepare.java new file mode 100644 index 000000000..e74d99d8b --- /dev/null +++ b/designer-base/src/main/java/com/fr/design/deeplink/DeepLinkPrepare.java @@ -0,0 +1,17 @@ +package com.fr.design.deeplink; + +import java.util.Map; + +/** + * created by Harrison on 2022/08/23 + **/ +public interface DeepLinkPrepare { + + /** + * 准备 + * + * @param url 链接 + * @return 成功/失败 + */ + boolean prepare(String url, String host, String path, Map params); +} diff --git a/designer-base/src/main/java/com/fr/design/dialog/UIDialog.java b/designer-base/src/main/java/com/fr/design/dialog/UIDialog.java index 458daddab..63aea87c3 100644 --- a/designer-base/src/main/java/com/fr/design/dialog/UIDialog.java +++ b/designer-base/src/main/java/com/fr/design/dialog/UIDialog.java @@ -18,6 +18,7 @@ import java.awt.BorderLayout; import java.awt.Dialog; import java.awt.FlowLayout; import java.awt.Frame; +import java.awt.Window; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.KeyEvent; @@ -44,6 +45,10 @@ public abstract class UIDialog extends JDialog { super(parent); } + public UIDialog(Window parent) { + super(parent); + } + public UIDialog(Frame parent, BasicPane pane) { this(parent, pane, true); diff --git a/designer-base/src/main/java/com/fr/design/dialog/link/MessageWithLink.java b/designer-base/src/main/java/com/fr/design/dialog/link/MessageWithLink.java index d412fb682..3e4175058 100644 --- a/designer-base/src/main/java/com/fr/design/dialog/link/MessageWithLink.java +++ b/designer-base/src/main/java/com/fr/design/dialog/link/MessageWithLink.java @@ -22,7 +22,12 @@ import static com.fr.design.utils.LinkStrUtils.LABEL; * Created by hades on 2020/10/23 */ public class MessageWithLink extends JEditorPane { - + + private static final String HTML_BODY = " 的话,将会自动点击匹配 url @@ -69,6 +74,34 @@ public class MessageWithLink extends JEditorPane { this(frontMessage, linkName, link, backMessage, color, font, LABEL.getForeground()); } + public MessageWithLink(String htmlText, Font font) { + this(setHtmlFont(htmlText, font)); + } + + + + /** + * 将指定的字体赋给html文本 + * 任何失败,返回原html文本 + * */ + private static String setHtmlFont(String htmlText, Font font) { + + try { + int bodyIndex = htmlText.indexOf(HTML_BODY); + StringBuilder sb = new StringBuilder(); + String left = htmlText.substring(0, bodyIndex + HTML_BODY.length()); + String right = htmlText.substring(bodyIndex + HTML_BODY.length()); + sb.append(left); + sb.append(String.format(HTML_STYLE_FORMAT, LinkStrUtils.generateStyle(Color.WHITE, font, Color.BLACK))); + sb.append(right); + return sb.toString(); + } catch (Exception e) { + FineLoggerFactory.getLogger().error(e, e.getMessage()); + } + + return htmlText; + } + public MessageWithLink(String frontMessage, String linkName, String link, String backMessage, Color backgroundColor, Font font, Color fontColor) { super("text/html", "" + frontMessage + "" + linkName + "" + backMessage + ""); 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..9f303c083 --- /dev/null +++ b/designer-base/src/main/java/com/fr/design/file/DefaultTemplateTreeDefineProcessor.java @@ -0,0 +1,584 @@ +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.TemplateUtils; +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.ArrayUtils; +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 (pasteNodes.isEmpty()) { + //提示:复制的文件都不能黏贴 + FineJOptionPane.showMessageDialog(DesignerContext.getDesignerFrame(), + Toolkit.i18nText("Fine-Design_Basic_Confirm_Paste_Unlock_File"), + Toolkit.i18nText("Fine-Design_Basic_Alert"), + WARNING_MESSAGE); + return; + } + if (lockedNodes.isEmpty()) { + doPaste(targetDir, pasteNodes); + } else { + if (FineJOptionPane.showConfirmDialog(DesignerContext.getDesignerFrame(), + Toolkit.i18nText("Fine-Design_Basic_Confirm_Paste_Other_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().refreshParent(); + 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 (!selectedOperation.access()) { + FineJOptionPane.showMessageDialog(DesignerContext.getDesignerFrame(), + Toolkit.i18nText("Fine-Design_Basic_Template_Permission_Denied"), + Toolkit.i18nText("Fine-Design_Basic_Alert"), + WARNING_MESSAGE); + return false; + } + //检测之前复制的文件是否被打开 + if (TemplateUtils.checkTemplateIsEditing(treeNodeList.toArray(new ExpandMutableTreeNode[0]))) { + return FineJOptionPane.showConfirmDialog(DesignerContext.getDesignerFrame(), + Toolkit.i18nText("Fine-Design_Basic_Template_Is_Editing"), + Toolkit.i18nText("Fine-Design_Basic_Alert"), + YES_NO_OPTION) == JOptionPane.YES_OPTION; + } + return true; + } + + + private void doPaste(String targetDir, List pasteNodes) { + try { + if (StringUtils.isEmpty(targetDir)) { + 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(false); + this.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE); + GUICoreUtils.setWindowCenter(DesignerContext.getDesignerFrame(), this); + this.setVisible(true); + } + + private void confirmClose() { + //获取目录树中所选中的文件,并判断是否有权限 + if (!checkBeforeMove()) { + 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 checkBeforeMove() { + if (getDirTree().getSelectionCount() != 0 && !TemplateDirTreePane.getInstance().selectedAccess()) { + FineJOptionPane.showMessageDialog(this, + Toolkit.i18nText("Fine-Design_Basic_Template_Permission_Denied"), + Toolkit.i18nText("Fine-Design_Basic_Alert"), + WARNING_MESSAGE); + return false; + } + if (TemplateUtils.checkSelectedTemplateIsEditing()) { + return FineJOptionPane.showConfirmDialog(this, + Toolkit.i18nText("Fine-Design_Basic_Template_Is_Editing"), + Toolkit.i18nText("Fine-Design_Basic_Alert"), + YES_NO_OPTION) == JOptionPane.YES_OPTION; + } + return true; + } + + private boolean doMove() { + FileNode fileNode = getDirTree().getSelectedFileNode(); + ExpandMutableTreeNode rootTreeNode = (ExpandMutableTreeNode) getDirTree().getModel().getRoot(); + fileNode = fileNode == null ? (FileNode) rootTreeNode.getUserObject() : 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); + if (ArrayUtils.isEmpty(fileNodes)) { + //空目录:相当于新建一个目录 + DesignerFrameFileDealerPane.getInstance().getSelectedOperation().mkdir(targetDir); + } + 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", sourcePath), + 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/HistoryTemplateListCache.java b/designer-base/src/main/java/com/fr/design/file/HistoryTemplateListCache.java index 296c1f2de..0b093e4ef 100644 --- a/designer-base/src/main/java/com/fr/design/file/HistoryTemplateListCache.java +++ b/designer-base/src/main/java/com/fr/design/file/HistoryTemplateListCache.java @@ -8,6 +8,7 @@ import com.fr.design.data.DesignTableDataManager; import com.fr.design.i18n.Toolkit; import com.fr.design.mainframe.DesignerContext; import com.fr.design.mainframe.DesignerFrameFileDealerPane; +import com.fr.design.mainframe.JNullTemplate; import com.fr.design.mainframe.JTemplate; import com.fr.design.mainframe.JVirtualTemplate; import com.fr.design.ui.util.UIUtil; @@ -19,6 +20,7 @@ import com.fr.plugin.context.PluginContext; import com.fr.stable.CoreConstants; import com.fr.stable.StringUtils; import com.fr.third.org.apache.commons.io.FilenameUtils; +import org.jetbrains.annotations.Nullable; import java.util.ArrayList; import java.util.Collections; @@ -26,7 +28,6 @@ import java.util.HashMap; import java.util.List; import java.util.ListIterator; import java.util.Map; -import java.util.Set; /** * 历史模板缓存 @@ -97,8 +98,14 @@ public class HistoryTemplateListCache implements CallbackEvent { FineLoggerFactory.getLogger().error(e.getMessage(), e); } } - - + + + /** + * 需要使用 {@link JTemplate#isValid(JTemplate)} 来判断空 + * + * @return 当前正在编辑的模板 + */ + @Nullable public JTemplate getCurrentEditingTemplate() { return this.editingTemplate; } @@ -109,8 +116,11 @@ public class HistoryTemplateListCache implements CallbackEvent { */ public void setCurrentEditingTemplate(JTemplate jt) { this.editingTemplate = jt; + + if (!JTemplate.isValid(jt)) { + return; + } //如果当前历史面板中没有 - if (contains(jt) == -1) { addHistory(); } @@ -303,7 +313,7 @@ public class HistoryTemplateListCache implements CallbackEvent { JTemplate template; template = this.getCurrentEditingTemplate(); - if (template != null) { + if (template != null && !(template instanceof JNullTemplate)) { String editingPath = FilenameUtils.standard(template.getEditingFILE().getPath()); if (isDir ? editingPath.contains(from + CoreConstants.SEPARATOR) : editingPath.equals(from)) { FILE renameFile = template.getEditingFILE(); 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..480edea34 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,14 +4,18 @@ 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; import com.fr.design.mainframe.JTemplate; import com.fr.design.mainframe.TemplateSavingChecker; +import com.fr.design.mainframe.manager.search.TemplateTreeSearchManager; import com.fr.design.utils.DesignUtils; import com.fr.design.utils.gui.GUICoreUtils; import com.fr.design.utils.gui.GUIPaintUtils; @@ -24,6 +28,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 +41,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 +57,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; @@ -59,6 +68,11 @@ import java.awt.geom.Path2D; import java.awt.geom.RoundRectangle2D; import java.util.List; +import static com.fr.design.dialog.FineJOptionPane.showConfirmDialog; +import static javax.swing.JOptionPane.OK_CANCEL_OPTION; +import static javax.swing.JOptionPane.OK_OPTION; +import static javax.swing.JOptionPane.WARNING_MESSAGE; + /** * Author : daisy * Date: 13-8-5 @@ -160,6 +174,192 @@ 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) { + //处于搜索模式时,先退出搜索模式,再定位 + if (TemplateTreeSearchManager.getInstance().isInSearchMode()) { + TemplateTreeSearchManager.getInstance().outOfSearchMode(); + TemplateTreePane.getInstance().refreshDockingView(); + } + JTemplate template = openedTemplate.get(this.tplIndex); + locateTemplate(template); + } + + private void locateTemplate(JTemplate template) { + FILE currentTemplate = template.getEditingFILE(); + //模板不属于当前环境,跟预览一样先提示保存,再定位模板 + if (!currentTemplate.exists()) { + int selVal = showConfirmDialog( + DesignerContext.getDesignerFrame(), + Toolkit.i18nText("Fine-Design_Basic_Web_Preview_Message"), + Toolkit.i18nText("Fine-Design_Basic_Preview_Tool_Tips"), + OK_CANCEL_OPTION, + WARNING_MESSAGE + ); + if (OK_OPTION == selVal) { + CallbackSaveWorker worker = template.saveAs(); + worker.start(template.getRuntimeId()); + worker.addSuccessCallback(new Runnable() { + @Override + public void run() { + gotoEditingTemplateLeaf(template.getPath()); + } + }); + } + } else { + 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 (option == CloseOption.All) { + 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..08531b895 --- /dev/null +++ b/designer-base/src/main/java/com/fr/design/file/TemplateDirTreePane.java @@ -0,0 +1,82 @@ +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..9bace5945 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,8 @@ import com.fr.design.mainframe.DesignerContext; import com.fr.design.mainframe.DesignerFrameFileDealerPane; import com.fr.design.lock.LockInfoDialog; import com.fr.design.mainframe.JTemplate; +import com.fr.design.mainframe.manager.search.TemplateTreeSearchManager; +import com.fr.design.mainframe.manager.search.searcher.control.pane.TemplateSearchRemindPane; import com.fr.file.FILE; import com.fr.file.FileNodeFILE; import com.fr.file.filetree.FileNode; @@ -57,6 +58,9 @@ import java.util.Enumeration; import java.util.Objects; import java.util.Observable; import java.util.Observer; +import java.util.Set; +import java.util.stream.Collectors; + import org.jetbrains.annotations.Nullable; @@ -74,19 +78,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 +117,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 +297,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") + "!"); + } + + /** * 删除文件 * 文件夹和文件均可删除 @@ -362,16 +389,22 @@ public class TemplateTreePane extends JPanel implements FileOperations { } } } - refreshAfterDelete(); + Set deletedFileNode = deletableNodes.stream().map(treeNode -> (FileNode) treeNode.getUserObject()).collect(Collectors.toSet()); + refreshAfterDelete(deletedFileNode); } - private void refreshAfterDelete() { - TreePath[] paths = reportletsTree.getSelectionPaths(); - if (paths == null) { - reportletsTree.refresh(); - } else { - for (TreePath path : Objects.requireNonNull(reportletsTree.getSelectionPaths())) { - reportletsTree.refreshParent(path); + private void refreshAfterDelete(Set deletedPaths) { + if (TemplateTreeSearchManager.getInstance().isInSearchMode()) { + TemplateTreeSearchManager.getInstance().deleteMatchedNode(deletedPaths); + TemplateTreeSearchManager.getInstance().updateTemplateTree(); + } else { + TreePath[] paths = reportletsTree.getSelectionPaths(); + if (paths == null) { + reportletsTree.refresh(); + } else { + for (TreePath path : Objects.requireNonNull(reportletsTree.getSelectionPaths())) { + reportletsTree.refreshParent(path); + } } } } @@ -519,9 +552,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/date/UICalendarPanel.java b/designer-base/src/main/java/com/fr/design/gui/date/UICalendarPanel.java index fbbe38ce4..fb2102117 100644 --- a/designer-base/src/main/java/com/fr/design/gui/date/UICalendarPanel.java +++ b/designer-base/src/main/java/com/fr/design/gui/date/UICalendarPanel.java @@ -2,6 +2,8 @@ package com.fr.design.gui.date; import com.fr.base.BaseUtils; import com.fr.base.background.GradientBackground; +import com.fr.design.carton.MonthlyCartonFile; +import com.fr.design.carton.SwitchForSwingChecker; import com.fr.design.constants.UIConstants; import com.fr.design.gui.ibutton.UIButton; import com.fr.design.gui.ilable.UILabel; @@ -9,7 +11,10 @@ import com.fr.design.gui.itextfield.UITextField; import com.fr.design.layout.FRGUIPaneFactory; import com.fr.design.utils.DesignUtils; import com.fr.design.utils.gui.GUIPaintUtils; +import com.fr.general.GeneralUtils; import com.fr.stable.Constants; +import com.fr.stable.ProductConstantsBase; +import com.fr.stable.StableUtils; import com.fr.stable.StringUtils; import javax.swing.BorderFactory; @@ -40,16 +45,20 @@ import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.awt.event.MouseListener; import java.awt.geom.RoundRectangle2D; +import java.io.File; import java.text.SimpleDateFormat; import java.util.Calendar; import java.util.Date; +import java.util.HashSet; +import java.util.Set; public class UICalendarPanel extends JPanel { private static final Font FONT_UI = DesignUtils.getDefaultGUIFont().applySize(12); private static final Font FONT_BLACK = new Font(com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Basic_Black_Font"), Font.PLAIN, 12); private static final int WEEKDAY_COUNT = 7; private static final int TOTAL_DAYS_COUNT = 42; - + //卡顿日志所在地址 + private static final String JOURNAL_FILE_PATH = StableUtils.pathJoin(ProductConstantsBase.getEnvHome(), "journal_log"); protected Color selectedBackground; protected Color selectedForeground; protected Color background; @@ -63,12 +72,27 @@ public class UICalendarPanel extends JPanel { private boolean isSupportDateChangeListener = false; private java.util.Date selectedDate = null; private boolean isTimePicker; + private final Color LABEL_FORGE_GROUND = new Color(0x6F6F6); + private MouseListener todayListener; + private UIDayLabel lbToday; + /** + * 一个int类型用于判断日历是否是专门用于处理卡顿的设计器反馈箱中的日历 + * 0表示是 + * -1表示不是 + */ + private boolean speciallyForCarton = false; /** * 年月格式 */ final SimpleDateFormat monthFormat = new SimpleDateFormat("yyyy-MM"); + /** + * 年月格式2 + */ + final SimpleDateFormat dayFormat + = new SimpleDateFormat("yyyy-MM-dd"); + public UICalendarPanel() { this(new Date(), false); } @@ -77,6 +101,14 @@ public class UICalendarPanel extends JPanel { this(new Date(), isTimerPicker); } + /** + * 构造函数,用于给speciallyForCarton赋值 + */ + public UICalendarPanel(boolean isTimePicker, boolean speciallyForCarton) { + this(new Date(), isTimePicker); + this.speciallyForCarton = speciallyForCarton; + initTodayListener(); + } public UICalendarPanel(Date selectedDate, boolean isTimerPicker) { this.selectedDate = selectedDate; @@ -106,7 +138,12 @@ public class UICalendarPanel extends JPanel { updateHMS(); } } - + private void initTodayListener() { + if (speciallyForCarton && !SwitchForSwingChecker.isCartonExists()) { + lbToday.setEnabled(false); + lbToday.removeMouseListener(todayListener); + } + } // << < yyyy/MM/dd > >> private JPanel createNorthPane() { JPanel pNorth = FRGUIPaneFactory.createX_AXISBoxInnerContainer_S_Pane(); @@ -187,13 +224,13 @@ public class UICalendarPanel extends JPanel { GradientPane pToday = new GradientPane(new GradientBackground(new Color(0x097BDA), new Color(0x40A3EE), GradientBackground.TOP2BOTTOM), false); pToday.setPreferredSize(new Dimension(216, 18)); pToday.setLayout(new BorderLayout()); - UIDayLabel lbToday = new UIDayLabel(new Date(), false); + lbToday = new UIDayLabel(new Date(), false); lbToday.setForeground(new Color(0x000000)); - lbToday.addMouseListener(createTodayListener(pToday, lbToday)); + todayListener = createTodayListener(pToday, lbToday); + lbToday.addMouseListener(todayListener); pToday.setBackground(new Color(0xF0F0F0)); pToday.add(lbToday, BorderLayout.CENTER); pCenter.add(pToday, BorderLayout.SOUTH); - return pCenter; } @@ -288,10 +325,67 @@ public class UICalendarPanel extends JPanel { }; } + /** + *根据当月获取下个月 + * 圈复杂度比较高就单独拿出来了 + */ + private int getNextMonth(int month) { + int num = (month + 1) % 12; + return num == 0 ? 12 : num; + } + private int getYearOfNextMonth(int month, int year) { + return year + (month + 1) / 13; + } + /** + * 将配置目录中已有的所有卡顿日志文件的名字如(2022-08-09)加入到set中 + */ + private void addFileIntoSet(Set set, MonthlyCartonFile monthlyCartonFile) { + File currentMonthFile = monthlyCartonFile.getCurrentMonthFile(); + File lastMonthFile = monthlyCartonFile.getLastMonthFile(); + File nextMonthFile = monthlyCartonFile.getNextMonthFile(); + File[] monthFiles = new File[3]; + monthFiles[0] = lastMonthFile; + monthFiles[1] = currentMonthFile; + monthFiles[2] = nextMonthFile; + for (File file : monthFiles) { + if (file.exists() && file.isDirectory()) { + File[] files = file.listFiles(); + for (File detailFile : files) { + set.add(detailFile.getName()); + } + } + } + } + /** + * 给label设置属性 + */ + private void setUIDayLabel(UIDayLabel label, boolean isCurrentMonth, + Calendar setupCalendar, Set logSet) { + /** + * 区分开两种panel + */ + if (speciallyForCarton) { + if (!logSet.contains(GeneralUtils.objectToString(dayFormat.format(setupCalendar.getTime())))) { + label.setEnabled(false); + } else { + label.addMouseListener(dayBttListener); + } + } else { + label.addMouseListener(dayBttListener); + label.setEnabled(isCurrentMonth); + } + if (!isCurrentMonth) { + label.setForeground(LABEL_FORGE_GROUND); + } + } /** * 更新日期 */ protected void updateDays() { + /** + * 用于处理卡顿日志日历的一些工具 + */ + Set logSet = new HashSet<>(); //更新月份 monthLabel.setText(monthFormat.format(calendar.getTime())); days.removeAll(); @@ -302,8 +396,29 @@ public class UICalendarPanel extends JPanel { setupCalendar.set(Calendar.DAY_OF_MONTH, 1); int first = setupCalendar.get(Calendar.DAY_OF_WEEK); setupCalendar.add(Calendar.DATE, -first); - boolean isCurrentMonth = false; + if (speciallyForCarton) { + Calendar clone = (Calendar)setupCalendar.clone(); + clone.add(Calendar.DATE, 1); + int year = clone.get(Calendar.YEAR); + //日历获取的月份是从0开始的 + int month = clone.get(Calendar.MONTH) + 1; + //往后推一个月的年月份 + int month2 = getNextMonth(month); + int year2 = getYearOfNextMonth(month, year); + //再往后推一个月的年月份 + int month3 = getNextMonth(month2); + int year3 = getYearOfNextMonth(month2, year2); + //文件地址如:"C:\Users\23131\.FineReport110\journal_log\2022\month-9\2022-09-01" + MonthlyCartonFile monthlyCartonFile = new MonthlyCartonFile(); + monthlyCartonFile.setCurrentMonthFile + (new File(StableUtils.pathJoin(JOURNAL_FILE_PATH, String.valueOf(year2), "month-" + month2))); + monthlyCartonFile.setLastMonthFile + (new File(StableUtils.pathJoin(JOURNAL_FILE_PATH, String.valueOf(year), "month-" + month))); + monthlyCartonFile.setNextMonthFile + (new File(StableUtils.pathJoin(JOURNAL_FILE_PATH, String.valueOf(year3), "month-" + month3))); + addFileIntoSet(logSet, monthlyCartonFile); + } for (int i = 0; i < TOTAL_DAYS_COUNT; i++) { setupCalendar.add(Calendar.DATE, 1); GradientPane gp = new GradientPane(new GradientBackground(new Color(0xFEFEFE), new Color(0xF3F2F3), GradientBackground.TOP2BOTTOM), true); @@ -313,14 +428,10 @@ public class UICalendarPanel extends JPanel { UIDayLabel label = new UIDayLabel(setupCalendar.getTime()); label.setHorizontalAlignment(SwingConstants.RIGHT); label.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 9)); - label.addMouseListener(dayBttListener); if ("1".equals(label.getText())) { isCurrentMonth = !isCurrentMonth; } - label.setEnabled(isCurrentMonth); - if (!isCurrentMonth) { - label.setForeground(new Color(0x6F6F6)); - } + setUIDayLabel(label, isCurrentMonth, setupCalendar, logSet); //当前选择的日期 if (setupCalendar.get(Calendar.DAY_OF_MONTH) == selectedCalendar.get(Calendar.DAY_OF_MONTH) && isCurrentMonth) { gp.setGradientBackground(new GradientBackground(new Color(0x097BD9), new Color(0x41A3EE), GradientBackground.TOP2BOTTOM)); @@ -534,6 +645,7 @@ public class UICalendarPanel extends JPanel { this.setLayout(new GridLayout(6, 7, 1, 1)); this.setBackground(new Color(0xFFFFFF)); this.setBorder(BorderFactory.createMatteBorder(1, 0, 1, 0, new Color(0xDADADA))); + } public void paint(Graphics g) { @@ -754,7 +866,6 @@ public class UICalendarPanel extends JPanel { public static void main(String[] args) { JFrame frame = new JFrame(); - UICalendarPanel calendarPanel = new UICalendarPanel(); final UITextField field = new UITextField(); field.setPreferredSize(new Dimension(120, 25)); diff --git a/designer-base/src/main/java/com/fr/design/gui/date/UIDatePicker.java b/designer-base/src/main/java/com/fr/design/gui/date/UIDatePicker.java index be3ef20a3..1da26c302 100644 --- a/designer-base/src/main/java/com/fr/design/gui/date/UIDatePicker.java +++ b/designer-base/src/main/java/com/fr/design/gui/date/UIDatePicker.java @@ -7,6 +7,7 @@ import com.fr.design.utils.gui.GUICoreUtils; import com.fr.general.ComparatorUtils; import com.fr.log.FineLoggerFactory; import com.fr.stable.StringUtils; +import com.fr.design.carton.FeedbackToolboxDialog; import javax.swing.BorderFactory; import javax.swing.JComboBox; @@ -32,6 +33,11 @@ import java.util.Date; * UIDatePicker */ public class UIDatePicker extends UIComboBox implements Serializable { + + /** + * 用于记录本datePicker是否是由设计器卡顿优化箱上打开(后续要去调用方法) + */ + private FeedbackToolboxDialog feedbackToolboxDialog = null; /** * 日期格式类型 */ @@ -40,7 +46,7 @@ public class UIDatePicker extends UIComboBox implements Serializable { public static final int STYLE_CN_DATETIME = 2; public static final int STYLE_CN_DATETIME1 = 3; public static final int STYLE_EN_DATE = 4; - public boolean isWillHide = false; + private boolean willHide = false; /** * 日期格式类型 */ @@ -67,23 +73,43 @@ public class UIDatePicker extends UIComboBox implements Serializable { this(formatStyle, new Date()); } - public UIDatePicker(int formatStyle, Date initialDatetime) throws UnsupportedOperationException { + public UIDatePicker(int formatStyle, FeedbackToolboxDialog feedbackToolboxDialog) throws UnsupportedOperationException { + this(formatStyle, new Date(), feedbackToolboxDialog); + } + + /** + * + * @param formatStyle + * @param initialDatetime + * @param feedbackToolboxDialog 判断该日历是否由设计器反馈工具箱打开 + * @throws UnsupportedOperationException + */ + public UIDatePicker(int formatStyle, Date initialDatetime, FeedbackToolboxDialog feedbackToolboxDialog) throws UnsupportedOperationException { this.setStyle(formatStyle); //设置可编辑 this.setEditable(true); - this.setCursor(new Cursor(Cursor.DEFAULT_CURSOR)); - //设置编辑器属性(只能输入正确日期) JTextField textField = ((JTextField) getEditor().getEditorComponent()); - textField.setHorizontalAlignment(SwingConstants.CENTER); - dateDocument = new JDateDocument(textField, this.dateFormat); + if (feedbackToolboxDialog != null) { + dateDocument = new JDateDocument(textField, this.dateFormat, StringUtils.EMPTY); + this.feedbackToolboxDialog = feedbackToolboxDialog; + textField.setHorizontalAlignment(SwingConstants.LEFT); + //设置当前选择日期 + this.setSelectedItem(initialDatetime); + } else { + dateDocument = new JDateDocument(textField, this.dateFormat); + this.setSelectedItem(initialDatetime == null ? new Date() : initialDatetime); + textField.setHorizontalAlignment(SwingConstants.CENTER); + } textField.setDocument(dateDocument); //设置Model为单值Model this.setModel(model); - //设置当前选择日期 - this.setSelectedItem(initialDatetime == null ? new Date() : initialDatetime); - updateUI(); + updateUI(); + } + + public UIDatePicker(int formatStyle, Date initialDatetime) throws UnsupportedOperationException { + this(formatStyle, initialDatetime, null); } /** @@ -149,20 +175,20 @@ public class UIDatePicker extends UIComboBox implements Serializable { * @return Date */ public Date getSelectedDate() throws ParseException { - synchronized (this) { - return dateFormat.parse(getSelectedItem().toString()); - } + synchronized (this) { + return dateFormat.parse(getSelectedItem().toString()); + } } /** * 设置当前选择的日期 */ public synchronized void setSelectedDate(Date date) throws ParseException { - if (date == null) { - this.setSelectedItem(null); - } else { - this.setSelectedItem(dateFormat.format(date)); - } + if (date == null) { + this.setSelectedItem(null); + } else { + this.setSelectedItem(dateFormat.format(date)); + } } @Override @@ -182,34 +208,36 @@ public class UIDatePicker extends UIComboBox implements Serializable { */ class DatePopup extends BasicComboPopup implements ChangeListener { UICalendarPanel calendarPanel = null; - public DatePopup(JComboBox box) { super(box); - setLayout(FRGUIPaneFactory.createBorderLayout()); - calendarPanel = new UICalendarPanel(formatStyle > 1); + if (feedbackToolboxDialog != null) { + calendarPanel = new UICalendarPanel(formatStyle > 1, true); + } else { + calendarPanel = new UICalendarPanel(formatStyle > 1); + } calendarPanel.addDateChangeListener(this); add(calendarPanel, BorderLayout.CENTER); setBorder(BorderFactory.createEmptyBorder()); } @Override - public void hide() { - if (isWillHide) { - super.hide(); - } - } + public void hide() { + if (willHide) { + super.hide(); + } + } - @Override - public void show() { - if (isWillHide || UIDatePicker.this.isEnabled() == false) { - return; - } - if (calendarPanel != null) { - calendarPanel.resetHMSPaneSelectedNumberField(); - } - super.show(); - } + @Override + public void show() { + if (willHide || UIDatePicker.this.isEnabled() == false) { + return; + } + if (calendarPanel != null) { + calendarPanel.resetHMSPaneSelectedNumberField(); + } + super.show(); + } /** * 显示弹出面板 @@ -223,16 +251,16 @@ public class UIDatePicker extends UIComboBox implements Serializable { && ComparatorUtils.equals(newValue, Boolean.TRUE)) { //SHOW try { String strDate = comboBox.getSelectedItem().toString(); - synchronized (this) { - Date selectionDate = new Date(); - if (StringUtils.isNotBlank(strDate)) { + synchronized (this) { + Date selectionDate = new Date(); + if (StringUtils.isNotBlank(strDate)) { selectionDate = dateFormat.parse(strDate); } calendarPanel.setSelectedDate(selectionDate); - calendarPanel.updateHMS(); - } + calendarPanel.updateHMS(); + } } catch (Exception e) { - FineLoggerFactory.getLogger().error(e.getMessage(), e); + FineLoggerFactory.getLogger().error(e.getMessage(), e); } } else if (ComparatorUtils.equals(oldValue, Boolean.TRUE) && ComparatorUtils.equals(newValue, Boolean.FALSE)) { //HIDE @@ -241,16 +269,21 @@ public class UIDatePicker extends UIComboBox implements Serializable { super.firePropertyChange(propertyName, oldValue, newValue); } + @Override public void stateChanged(ChangeEvent e) { - if (calendarPanel.getSelectedDate() != null && dateFormat != null) { - String strDate = dateFormat.format(calendarPanel.getSelectedDate()); - if (comboBox.isEditable() && comboBox.getEditor() != null) { - comboBox.configureEditor(comboBox.getEditor(), strDate); - } - comboBox.setSelectedItem(strDate); - } - comboBox.repaint(); - setVisible(false); + if (calendarPanel.getSelectedDate() != null && dateFormat != null) { + String strDate = dateFormat.format(calendarPanel.getSelectedDate()); + if (comboBox.isEditable() && comboBox.getEditor() != null) { + comboBox.configureEditor(comboBox.getEditor(), strDate); + } + comboBox.setSelectedItem(strDate); + } + comboBox.repaint(); + setVisible(false); + //选择完日期,导出/上传按钮变可用 + if (feedbackToolboxDialog != null) { + feedbackToolboxDialog.setSwitches(true); + } } } @@ -258,53 +291,53 @@ public class UIDatePicker extends UIComboBox implements Serializable { protected ComboBoxUI getUIComboBoxUI() { return new UIComboBoxUI() { @Override - protected ComboPopup createPopup() { - return new DatePopup(comboBox); - } - @Override - public void mousePressed(MouseEvent e) { - if (UIDatePicker.this.isPopupVisible()) { - isWillHide = true; - UIDatePicker.this.hidePopup(); - } else { - isWillHide = false; - UIDatePicker.this.showPopup(); - } - } - }; + protected ComboPopup createPopup() { + return new DatePopup(comboBox); + } + @Override + public void mousePressed(MouseEvent e) { + if (UIDatePicker.this.isPopupVisible()) { + willHide = true; + UIDatePicker.this.hidePopup(); + } else { + willHide = false; + UIDatePicker.this.showPopup(); + } + } + }; } - + //设置dataFormat public void setDateFormat(SimpleDateFormat format){ this.dateFormat = format; } - + //获取dateFormat public SimpleDateFormat getDateFormat(){ return this.dateFormat; } - + public JDateDocument getDateDocument(){ return this.dateDocument; } - public static void main(String[] args) { - LayoutManager layoutManager = null; - JFrame jf = new JFrame("test"); - jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); - JPanel content = (JPanel) jf.getContentPane(); - content.setLayout(layoutManager); - UIDatePicker bb = new UIDatePicker(); - if (args.length != 0) { - bb = new UIDatePicker(STYLE_CN_DATETIME); - } - bb.setEditable(true); - bb.setBounds(20, 20, bb.getPreferredSize().width, bb.getPreferredSize().height); - content.add(bb); - GUICoreUtils.centerWindow(jf); - jf.setSize(400, 400); - jf.setVisible(true); - } + public static void main(String[] args) { + LayoutManager layoutManager = null; + JFrame jf = new JFrame("test"); + jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + JPanel content = (JPanel) jf.getContentPane(); + content.setLayout(layoutManager); + UIDatePicker bb = new UIDatePicker(); + if (args.length != 0) { + bb = new UIDatePicker(STYLE_CN_DATETIME); + } + bb.setEditable(true); + bb.setBounds(20, 20, bb.getPreferredSize().width, bb.getPreferredSize().height); + content.add(bb); + GUICoreUtils.centerWindow(jf); + jf.setSize(400, 400); + jf.setVisible(true); + } } \ No newline at end of file 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..c4c5b7bf5 --- /dev/null +++ b/designer-base/src/main/java/com/fr/design/gui/itree/filetree/TemplateDirTree.java @@ -0,0 +1,53 @@ +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.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 = TemplateDirTreeSearchManager.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..d312beb91 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,115 @@ 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); + } + } + + /** + * 将搜索匹配到的模板添加到搜索结果树里面 + * + * @param root + * @param 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; + } + + private 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/layout/FRGUIPaneFactory.java b/designer-base/src/main/java/com/fr/design/layout/FRGUIPaneFactory.java index 401fae849..ce8fdaa9d 100644 --- a/designer-base/src/main/java/com/fr/design/layout/FRGUIPaneFactory.java +++ b/designer-base/src/main/java/com/fr/design/layout/FRGUIPaneFactory.java @@ -152,15 +152,18 @@ public class FRGUIPaneFactory { return new FRGridLayout(nColumn); } + public static LayoutManager createCenterLayout(JComponent centerBody) { + return createCenterLayout(centerBody, 0.5d, 0.3d); + } + /** * 将 centerBody 为中心,创建一个布局 * 注:只有当且仅当有一个组件,且希望组件 上下左右 居中时使用 * @param centerBody 中心组件 * @return 布局方式 */ - public static LayoutManager createCenterLayout(JComponent centerBody) { + public static LayoutManager createCenterLayout(JComponent centerBody, double factorX, double factorY) { - final double yFactor = 0.30; return new LayoutManager() { @Override @@ -185,8 +188,8 @@ public class FRGUIPaneFactory { // 这个时候大小是不确定的 int bodyWidth = centerBody.getPreferredSize().width; int bodyHeight = centerBody.getPreferredSize().height; - int labelX = (width - bodyWidth) / 2; - int labelY = (int) ((height - bodyHeight) * yFactor); + int labelX = (int) ((width - bodyWidth) * factorX); + int labelY = (int) ((height - bodyHeight) * factorY); centerBody.setBounds(labelX, labelY, bodyWidth, bodyHeight); } diff --git a/designer-base/src/main/java/com/fr/design/mainframe/CenterRegionContainerPane.java b/designer-base/src/main/java/com/fr/design/mainframe/CenterRegionContainerPane.java index 20c8c580a..0e06cc87a 100644 --- a/designer-base/src/main/java/com/fr/design/mainframe/CenterRegionContainerPane.java +++ b/designer-base/src/main/java/com/fr/design/mainframe/CenterRegionContainerPane.java @@ -156,7 +156,7 @@ public class CenterRegionContainerPane extends JPanel { private void addExtraButtons() { JTemplate jt = HistoryTemplateListCache.getInstance().getCurrentEditingTemplate(); - if (jt == null) { + if (!JTemplate.isValid(jt)) { return; } @@ -172,7 +172,7 @@ public class CenterRegionContainerPane extends JPanel { private void addCheckButton() { JTemplate jt = HistoryTemplateListCache.getInstance().getCurrentEditingTemplate(); - if (jt == null) { + if (!JTemplate.isValid(jt)) { return; } combineUp.addSeparator(new Dimension(2, 16)); @@ -185,7 +185,7 @@ public class CenterRegionContainerPane extends JPanel { private void addShareButton() { JTemplate jt = HistoryTemplateListCache.getInstance().getCurrentEditingTemplate(); - if (jt == null) { + if (!JTemplate.isValid(jt)) { return; } @@ -205,7 +205,7 @@ public class CenterRegionContainerPane extends JPanel { protected void checkCombineUp(boolean flag, ArrayList al) { //Yvan: 检查当前是否为WORK_SHEET状态,因为只有WORK_SHEET中含有格式刷组件,此时是不需要进行checkComponentsByNames的 JTemplate jTemplate = HistoryTemplateListCache.getInstance().getCurrentEditingTemplate(); - if (jTemplate != null) { + if (JTemplate.isValid(jTemplate)) { // 第一个条件满足后还需要添加一重判断,判断是编辑报表块还是参数面板,编辑报表块时则直接return if (jTemplate.getMenuState() == DesignState.WORK_SHEET && !jTemplate.isUpMode()) { return; diff --git a/designer-base/src/main/java/com/fr/design/mainframe/DesignerFrame.java b/designer-base/src/main/java/com/fr/design/mainframe/DesignerFrame.java index da8f28f60..dfd2a20cd 100644 --- a/designer-base/src/main/java/com/fr/design/mainframe/DesignerFrame.java +++ b/designer-base/src/main/java/com/fr/design/mainframe/DesignerFrame.java @@ -696,7 +696,7 @@ public class DesignerFrame extends JFrame implements JTemplateActionListener, Ta username = connection == null ? StringUtils.EMPTY : connection.getUserName(); } defaultTitleSB.append(username).append("@").append(envName).append("[").append(workspace.getDescription()).append("]"); - if (editingTemplate != null) { + if (JTemplate.isValid(editingTemplate)) { String path = editingTemplate.getPath(); if (!editingTemplate.getEditingFILE().exists()) { path = FILEFactory.MEM_PREFIX + path; @@ -815,6 +815,13 @@ public class DesignerFrame extends JFrame implements JTemplateActionListener, Ta basePane.remove(layout.getLayoutComponent(BorderLayout.EAST)); basePane.add(designerOpenEmptyPanel, BorderLayout.CENTER); + resetToolkitByPlus(ToolBarMenuDock.NULLAVOID); + + // 这里挺恶心的,是为了保证对插件的兼容性适配 + // 不然的话,插件就会 npe + // 见 https://work.fineres.com/browse/REPORT-76091 + HistoryTemplateListCache.getInstance().setCurrentEditingTemplate(JNullTemplate.NULL); + layeredPane.repaint(); } @@ -1081,7 +1088,7 @@ public class DesignerFrame extends JFrame implements JTemplateActionListener, Ta } JTemplate jt = HistoryTemplateListCache.getInstance().getCurrentEditingTemplate(); - if (jt != null) { + if (JTemplate.isValid(jt)) { DesignerEnvManager.getEnvManager().setLastOpenFile(jt.getEditingFILE().getPath()); } 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..e6bfff633 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 @@ -8,6 +8,9 @@ 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 +32,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; @@ -39,8 +44,6 @@ 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 +84,17 @@ 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>() { + + public static final com.fr.event.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 +150,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 +171,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); @@ -191,6 +207,16 @@ public class DesignerFrameFileDealerPane extends JPanel implements FileToolbarSt selectedOperation.refresh(); } + /** + * 刷新根目录并退出搜索模式 + */ + public void refreshAndOutOfSearch() { + if (TemplateTreeSearchManager.getInstance().isInSearchMode()) { + TemplateTreeSearchManager.getInstance().outOfSearchMode(); + } + refresh(); + } + private JPanel createUpToolBarPane() { JPanel panel = new JPanel(new BorderLayout()); panel.add(toolBar, BorderLayout.CENTER); @@ -285,6 +311,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 +449,37 @@ 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(); + refreshDockingView(); + } + } + /** * 版本管理 */ @@ -537,72 +595,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 +608,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 +740,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 +776,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 +790,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/JNullTemplate.java b/designer-base/src/main/java/com/fr/design/mainframe/JNullTemplate.java new file mode 100644 index 000000000..d10df810a --- /dev/null +++ b/designer-base/src/main/java/com/fr/design/mainframe/JNullTemplate.java @@ -0,0 +1,207 @@ +package com.fr.design.mainframe; + +import com.fr.design.DesignModelAdapter; +import com.fr.design.designer.TargetComponent; +import com.fr.design.gui.frpane.HyperlinkGroupPane; +import com.fr.design.gui.frpane.HyperlinkGroupPaneActionProvider; +import com.fr.design.gui.imenu.UIMenuItem; +import com.fr.design.mainframe.template.info.TemplateProcessInfo; +import com.fr.design.menu.ShortCut; +import com.fr.design.menu.ToolBarDef; + +import javax.swing.Icon; +import javax.swing.JComponent; +import javax.swing.JPanel; + +/** + * created by Harrison on 2022/08/09 + **/ +public class JNullTemplate extends JTemplate { + + public static final JTemplate NULL = new JNullTemplate(); + + @Override + public void copy() { + + } + + @Override + public boolean paste() { + return false; + } + + @Override + public boolean cut() { + return false; + } + + @Override + public AuthorityEditPane createAuthorityEditPane() { + return null; + } + + @Override + public JPanel getEastUpPane() { + return null; + } + + @Override + public JPanel getEastDownPane() { + return null; + } + + @Override + public ToolBarDef[] toolbars4Target() { + return new ToolBarDef[0]; + } + + @Override + public JComponent[] toolBarButton4Form() { + return new JComponent[0]; + } + + @Override + public void refreshEastPropertiesPane() { + + } + + @Override + public TargetComponent getCurrentElementCasePane() { + return null; + } + + @Override + public JComponent getCurrentReportComponentPane() { + return null; + } + + @Override + public TemplateProcessInfo getProcessInfo() { + return null; + } + + @Override + public void setJTemplateResolution(int resolution) { + + } + + @Override + public int getJTemplateResolution() { + return 0; + } + + @Override + protected JComponent createCenterPane() { + return null; + } + + @Override + public void removeTemplateSelection() { + + } + + @Override + public void refreshContainer() { + + } + + @Override + public void removeParameterPaneSelection() { + + } + + @Override + public void setScale(int resolution) { + + } + + @Override + public int getScale() { + return 0; + } + + @Override + public int selfAdaptUpdate() { + return 0; + } + + @Override + protected DesignModelAdapter createDesignModel() { + return null; + } + + @Override + public UIMenuItem[] createMenuItem4Preview() { + return new UIMenuItem[0]; + } + + @Override + protected BaseUndoState createUndoState() { + return null; + } + + @Override + protected void applyUndoState(BaseUndoState baseUndoState) { + + } + + @Override + public String suffix() { + return null; + } + + @Override + public ShortCut[] shortcut4TemplateMenu() { + return new ShortCut[0]; + } + + @Override + public ShortCut[] shortCuts4Authority() { + return new ShortCut[0]; + } + + @Override + public boolean isJWorkBook() { + return false; + } + + @Override + public HyperlinkGroupPane getHyperLinkPane(HyperlinkGroupPaneActionProvider hyperlinkGroupPaneActionProvider) { + return null; + } + + @Override + public HyperlinkGroupPane getHyperLinkPaneNoPop(HyperlinkGroupPaneActionProvider hyperlinkGroupPaneActionProvider) { + return null; + } + + @Override + public void setAuthorityMode(boolean isUpMode) { + + } + + @Override + public Icon getIcon() { + return null; + } + + @Override + public String route() { + return null; + } + + @Override + public JPanel[] toolbarPanes4Form() { + return new JPanel[0]; + } + + @Override + public JComponent toolBar4Authority() { + return null; + } + + @Override + public int getToolBarHeight() { + return 0; + } +} diff --git a/designer-base/src/main/java/com/fr/design/mainframe/JTemplate.java b/designer-base/src/main/java/com/fr/design/mainframe/JTemplate.java index eaf4b84bf..a44562114 100644 --- a/designer-base/src/main/java/com/fr/design/mainframe/JTemplate.java +++ b/designer-base/src/main/java/com/fr/design/mainframe/JTemplate.java @@ -1787,7 +1787,7 @@ public abstract class JTemplate> HistoryTemplateListCache.getInstance().getCurrentEditingTemplate().template.getTemplateID())) { refreshToolArea(); } - DesignerFrameFileDealerPane.getInstance().refresh(); + DesignerFrameFileDealerPane.getInstance().refreshAndOutOfSearch(); DesignerFrameFileDealerPane.getInstance().stateChange(); } }); @@ -1973,5 +1973,14 @@ public abstract class JTemplate> public void setDesignerUIMode() { DesignerUIModeConfig.getInstance().setAbsoluteMeasureUIMode(); } - + + /** + * 判断当前的模板是否是有效的模板 + * + * @param jt 模板 + * @return 是/否 + */ + public static boolean isValid(JTemplate jt) { + return jt != null && jt != JNullTemplate.NULL; + } } 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..e8f822b9a --- /dev/null +++ b/designer-base/src/main/java/com/fr/design/mainframe/manager/clip/TemplateTreeClipboard.java @@ -0,0 +1,66 @@ +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..67e798f6f --- /dev/null +++ b/designer-base/src/main/java/com/fr/design/mainframe/manager/search/TemplateDirTreeSearchManager.java @@ -0,0 +1,205 @@ +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.exitSearch(); + } + if (rendererHelper != null) { + rendererHelper.restore(getCurrentTemplateDirTree()); + } + } + + /** + * 所有匹配的目录节点 + * @return + */ + public FileNode[] matchesNode() { + return treeSearcher.getMatchSets().toArray(new FileNode[0]); + } + + /** + * 当前目录树中搜索的目录节点 + * @return + */ + public Map allFileNodes() { + return treeSearcher.getAllTemplates(); + } + + /** + * 搜索结果是否为空 + * @return + */ + 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; + } + + /** + * 当前是否处于搜索模式 + * @return + */ + 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..9375e5fb8 --- /dev/null +++ b/designer-base/src/main/java/com/fr/design/mainframe/manager/search/TemplateTreeSearchManager.java @@ -0,0 +1,242 @@ +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.Set; +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.exitSearch(); + } + 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 void deleteMatchedNode(Set fileNodes) { + treeSearcher.getMatchSets().removeAll(fileNodes); + } + + /** + * 当前模板树中的所有模板节点 + * @return + */ + public Map allFileNodes() { + return treeSearcher.getAllTemplates(); + } + + /** + * 是否处于搜索模式 + * @return + */ + public boolean isInSearchMode() { + return getTreeSearchStatus() != TreeSearchStatus.NOT_IN_SEARCH_MODE; + } + + /** + * 退出搜索模式 + */ + public void outOfSearchMode() { + restoreToolBarAndTreePane(); + } + + /** + * 搜索结果数是否处于更新状态 + * @return + */ + 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..fcad77380 --- /dev/null +++ b/designer-base/src/main/java/com/fr/design/mainframe/manager/search/searcher/TemplateDirTreeSearcher.java @@ -0,0 +1,195 @@ +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(); + } + + /** + * 将模板添加到已经计算过的集合中,不管模板是不是匹配 + * + * @param templateNames + */ + public synchronized void addToCalculatedSets(List templateNames) { + for (String templateName : templateNames) { + FileNode fileNode = allDirs.get(templateName); + if (fileNode == null) { + return; + } + calculatedSets.add(templateName); + } + } + + /** + * 将搜索匹配的目录添加到匹配的集合中 + * + * @param matchNodes + */ + public synchronized void addToMatchSets(List matchNodes) { + matchSets.addAll(matchNodes); + } + + /** + * 将目录添加到未计算的集合中 + * + * @param fileNodes + */ + public synchronized 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); + } + + /** + * 开始搜索 + * + * @param searchText + */ + @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 exitSearch() { + 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..90611c2b2 --- /dev/null +++ b/designer-base/src/main/java/com/fr/design/mainframe/manager/search/searcher/TemplateTreeSearcher.java @@ -0,0 +1,193 @@ +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.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.Executors; +import java.util.concurrent.ExecutorService; +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); + } + } + + /** + * 将搜索匹配的模板添加到匹配的集合中 + * + * @param matchNodes + */ + public synchronized void addToMatchSets(List matchNodes) { + matchSets.addAll(matchNodes); + } + + /** + * 将模板添加到未计算的集合中 + * + * @param fileNodes + */ + public synchronized 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); + } + + /** + * 开始搜索 + * + * @param searchText + */ + @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 exitSearch() { + 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..65ac5a020 --- /dev/null +++ b/designer-base/src/main/java/com/fr/design/mainframe/manager/search/searcher/control/common/TemplateDirSearchCallBack.java @@ -0,0 +1,55 @@ +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..925760f79 --- /dev/null +++ b/designer-base/src/main/java/com/fr/design/mainframe/manager/search/searcher/control/common/TemplateSearchCallBack.java @@ -0,0 +1,65 @@ +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..65014f133 --- /dev/null +++ b/designer-base/src/main/java/com/fr/design/mainframe/manager/search/searcher/control/common/TemplateSearchResult.java @@ -0,0 +1,130 @@ +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..a23775bbb --- /dev/null +++ b/designer-base/src/main/java/com/fr/design/mainframe/manager/search/searcher/control/common/TemplateSearchTask.java @@ -0,0 +1,78 @@ +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..9feefcb51 --- /dev/null +++ b/designer-base/src/main/java/com/fr/design/mainframe/manager/search/searcher/control/pane/TemplateDirSearchRemindPane.java @@ -0,0 +1,213 @@ +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(false, FlowLayout.CENTER, 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(570, 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(570, 20)); + notFoundPane.add(emptyPicLabel); + notFoundPane.add(textLabel); + + 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..07636ce63 --- /dev/null +++ b/designer-base/src/main/java/com/fr/design/mainframe/manager/search/searcher/control/pane/TemplateDirTreeSearchPane.java @@ -0,0 +1,152 @@ +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_Dir_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..bdb6a3281 --- /dev/null +++ b/designer-base/src/main/java/com/fr/design/mainframe/manager/search/searcher/control/pane/TemplateSearchRemindPane.java @@ -0,0 +1,228 @@ +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(); + + /** + * 搜索结束 + * @param matchSetsEmpty + */ + 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..bb98561bf --- /dev/null +++ b/designer-base/src/main/java/com/fr/design/mainframe/manager/search/searcher/control/pane/TemplateTreeSearchToolbarPane.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.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..b6d626e62 --- /dev/null +++ b/designer-base/src/main/java/com/fr/design/mainframe/manager/search/searcher/control/pre/TemplateDirPreSearchTask.java @@ -0,0 +1,35 @@ +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..83176f100 --- /dev/null +++ b/designer-base/src/main/java/com/fr/design/mainframe/manager/search/searcher/control/pre/TemplatePreSearchCallBack.java @@ -0,0 +1,35 @@ +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); + } + } + + /** + * 将结果添加到未计算的集合中 + * + * @param 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..496eef514 --- /dev/null +++ b/designer-base/src/main/java/com/fr/design/mainframe/manager/search/searcher/control/pre/TemplatePreSearchTask.java @@ -0,0 +1,62 @@ +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/mainframe/theme/utils/DefaultThemedTemplateCellElementCase.java b/designer-base/src/main/java/com/fr/design/mainframe/theme/utils/DefaultThemedTemplateCellElementCase.java index 64aaeb9c0..123875eef 100644 --- a/designer-base/src/main/java/com/fr/design/mainframe/theme/utils/DefaultThemedTemplateCellElementCase.java +++ b/designer-base/src/main/java/com/fr/design/mainframe/theme/utils/DefaultThemedTemplateCellElementCase.java @@ -33,7 +33,7 @@ public class DefaultThemedTemplateCellElementCase { private static DefaultTemplateCellElement themingCellElement(DefaultTemplateCellElement cellElement) { JTemplate template = HistoryTemplateListCache.getInstance().getCurrentEditingTemplate(); - if (template != null) { + if (JTemplate.isValid(template)) { TemplateTheme theme = template.getTemplateTheme(); ThemedCellStyle themedCellStyle = theme.getCellStyleList().getUse4Default(); if (themedCellStyle != null) { diff --git a/designer-base/src/main/java/com/fr/design/mainframe/toast/SimpleToast.java b/designer-base/src/main/java/com/fr/design/mainframe/toast/SimpleToast.java new file mode 100644 index 000000000..3764cbcd8 --- /dev/null +++ b/designer-base/src/main/java/com/fr/design/mainframe/toast/SimpleToast.java @@ -0,0 +1,194 @@ +package com.fr.design.mainframe.toast; + +import com.fr.concurrent.NamedThreadFactory; +import com.fr.design.dialog.UIDialog; +import com.fr.design.gui.ilable.UILabel; +import com.fr.design.layout.FRGUIPaneFactory; +import com.fr.module.ModuleContext; + +import javax.swing.BorderFactory; +import javax.swing.Icon; +import javax.swing.JPanel; +import javax.swing.SwingConstants; +import javax.swing.SwingUtilities; +import java.awt.BorderLayout; +import java.awt.Dimension; +import java.awt.Point; +import java.awt.Window; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; + +/** + * toast弹窗 + * */ +public class SimpleToast extends UIDialog { + private static final int MIN_HEIGHT = 36; + private static final String TOAST_MSG_TIMER = "TOAST_MSG_TIMER"; + private static final long DEFAULT_DISAPPEAR_DELAY = 5000; + private static final TimeUnit DEFAULT_TIME_UNIT = TimeUnit.MILLISECONDS; + + + private ScheduledExecutorService timer; + private int hideHeight = 0; + private JPanel contentPane; + private boolean show = false; + private Window parent; + private boolean autoDisappear; + + public SimpleToast(Window parent, Icon icon, String text, boolean autoDisappear) { + super(parent); + this.parent = parent; + this.autoDisappear = autoDisappear; + JPanel panel = createContentPane(icon, text); + init(panel); + } + + private JPanel createContentPane(Icon icon, String text) { + JPanel pane = FRGUIPaneFactory.createBorderLayout_S_Pane(); + + UILabel iconLabel = new UILabel(icon); + iconLabel.setVerticalAlignment(SwingConstants.TOP); + iconLabel.setBorder(BorderFactory.createEmptyBorder(3, 0, 0, 0)); + + + UILabel textLabel = new UILabel(text); + pane.add(iconLabel, BorderLayout.WEST); + pane.add(textLabel, BorderLayout.CENTER); + pane.setBorder(BorderFactory.createEmptyBorder(8, 15, 8, 15)); + + return pane; + } + + + private void init(JPanel panel) { + setFocusable(false); + setAutoRequestFocus(false); + setUndecorated(true); + contentPane = panel; + initComponent(); + } + + private void initComponent() { + this.getContentPane().setLayout(null); + this.getContentPane().add(contentPane); + Dimension dimension = calculatePreferSize(); + hideHeight = dimension.height; + setSize(new Dimension(dimension.width, 0)); + contentPane.setSize(dimension); + setRelativeLocation(dimension); + if (autoDisappear) { + disappear(contentPane); + } + } + + private void setRelativeLocation(Dimension dimension) { + int positionX = parent.getLocationOnScreen().x + (parent.getWidth() - dimension.width) / 2; + int positionY = parent.getLocationOnScreen().y + 10; + this.setLocation(positionX, positionY); + } + + private Dimension calculatePreferSize() { + Dimension contentDimension = contentPane.getPreferredSize(); + int height = Math.max(MIN_HEIGHT, contentDimension.height); + return new Dimension(contentDimension.width, height); + } + + + public void display(JPanel outerPanel) { + show = true; + outerPanel.setLocation(0, -hideHeight); + ScheduledExecutorService tipToolTimer = createToastScheduleExecutorService(); + tipToolTimer.scheduleAtFixedRate(new Runnable() { + @Override + public void run() { + SwingUtilities.invokeLater(()->{ + displayStep(outerPanel, tipToolTimer); + }); + } + }, 0, 50, TimeUnit.MILLISECONDS); + + } + + void displayStep(JPanel outerPanel, ScheduledExecutorService timer) { + Point point = outerPanel.getLocation(); + if (point.y >= 0 && !timer.isShutdown()) { + timer.shutdown(); + } + int showDistance = 5 + point.y < 0 ? 5 : -point.y; + outerPanel.setLocation(point.x, point.y + showDistance); + Dimension dimension = SimpleToast.this.getSize(); + SimpleToast.this.setSize(new Dimension(dimension.width, dimension.height + showDistance)); + } + + + + + + + private void disappear(JPanel outerPanel, long delay, TimeUnit timeUnit) { + timer = createToastScheduleExecutorService(); + timer.schedule(new DisappearMotion(outerPanel), delay, timeUnit); + } + + /** + * toast消失的动画效果 + * */ + class DisappearMotion implements Runnable { + JPanel panel; + + DisappearMotion(JPanel panel) { + this.panel = panel; + } + + @Override + public void run() { + ScheduledExecutorService tipToolTimer = createToastScheduleExecutorService(); + tipToolTimer.scheduleAtFixedRate(new Runnable() { + @Override + public void run() { + SwingUtilities.invokeLater(()->{ + disappearStep(tipToolTimer); + }); + } + }, 0, 50, TimeUnit.MILLISECONDS); + } + + void disappearStep(ScheduledExecutorService timer) { + Point point = panel.getLocation(); + if (point.y <= -hideHeight && !timer.isShutdown()) { + timer.shutdown(); + SimpleToast.this.setVisible(false); + SimpleToast.this.dispose(); + SimpleToast.this.show = false; + } + panel.setLocation(point.x, point.y - 5); + Dimension dimension = SimpleToast.this.getSize(); + SimpleToast.this.setSize(new Dimension(dimension.width, dimension.height - 5)); + } + } + + private void disappear(JPanel outerPanel) { + disappear(outerPanel, DEFAULT_DISAPPEAR_DELAY, DEFAULT_TIME_UNIT); + } + + private ScheduledExecutorService createToastScheduleExecutorService() { + return ModuleContext.getExecutor().newSingleThreadScheduledExecutor(new NamedThreadFactory(TOAST_MSG_TIMER)); + } + + @Override + public void checkValid() throws Exception { + } + + public void setVisible(boolean visible) { + super.setVisible(visible); + if (visible) { + display(contentPane); + } + } + + @Override + public void dispose() { + super.dispose(); + } + +} \ No newline at end of file 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/design/utils/ColorUtils.java b/designer-base/src/main/java/com/fr/design/utils/ColorUtils.java index 7c0daa111..f189ccc30 100644 --- a/designer-base/src/main/java/com/fr/design/utils/ColorUtils.java +++ b/designer-base/src/main/java/com/fr/design/utils/ColorUtils.java @@ -27,6 +27,38 @@ public class ColorUtils { } } } + + /** + * 递归的同步颜色,如何组件的背景颜色等于默认颜色的话,变更为 replaceColor + * + * @param component 组件 + * @param replaceColor 替换颜色 + * @param defaultColor 默认颜色 + */ + public static void syncBackgroundIfAbsent(Component component, Color replaceColor, Color defaultColor) { + + if (component.getBackground() != defaultColor) { + return; + } + component.setBackground(replaceColor); + if (component instanceof Container) { + Container container = (Container) component; + Component[] components = container.getComponents(); + if (components != null) { + Arrays.stream(components).forEach((e) -> syncBackgroundIfAbsent(e, replaceColor, defaultColor)); + } + } + } + + /** + * 使背景透明 + * + * @param component 组件 + */ + public static void transparentBackground(Component component) { + + syncBackgroundIfAbsent(component, new Color(0,0,0,0), ThemeUtils.BACK_COLOR); + } public static boolean isDarkColor(Color color) { if(color == null) { diff --git a/designer-base/src/main/java/com/fr/design/utils/DesignUtils.java b/designer-base/src/main/java/com/fr/design/utils/DesignUtils.java index 67fb91866..47690bba9 100644 --- a/designer-base/src/main/java/com/fr/design/utils/DesignUtils.java +++ b/designer-base/src/main/java/com/fr/design/utils/DesignUtils.java @@ -25,7 +25,7 @@ import com.fr.stable.StringUtils; import com.fr.stable.os.OperatingSystem; import com.fr.start.ServerStarter; import com.fr.start.common.DesignerStartupContext; -import com.fr.startup.ui.StartupPageModel; +import com.fr.start.common.DesignerStartupUtil; import com.fr.value.NotNullLazyValue; import com.fr.workspace.WorkContext; import org.jetbrains.annotations.NotNull; @@ -48,7 +48,6 @@ import java.net.URI; import java.nio.charset.StandardCharsets; import java.util.Enumeration; import java.util.Locale; -import java.util.Optional; import java.util.concurrent.Callable; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; @@ -216,18 +215,9 @@ public class DesignUtils { @Override public void run() { DesignerStartupContext context = DesignerStartupContext.getInstance(); + // 如果在启动页展示中 - if (context.isOnWaiting()) { - FileFILE fileFILE = new FileFILE(f); - // 设置上一次启动模板为 - DesignerEnvManager.getEnvManager().setLastOpenFile(fileFILE.getPath()); - StartupPageModel model = context.getStartupPageModel(); - Optional.ofNullable(model) - .ifPresent((e) -> { - // 执行上一次模板的启动 - Runnable openLastTemplateRunnable = e.getOpenLastTemplateRunnable(); - openLastTemplateRunnable.run(); - }); + if (DesignerStartupUtil.openTemplateIfOnWaiting(f)) { return; } // 如果是在启动中 diff --git a/designer-base/src/main/java/com/fr/design/utils/DesignerPort.java b/designer-base/src/main/java/com/fr/design/utils/DesignerPort.java index 28ab5e4c1..b57c54fbc 100644 --- a/designer-base/src/main/java/com/fr/design/utils/DesignerPort.java +++ b/designer-base/src/main/java/com/fr/design/utils/DesignerPort.java @@ -4,7 +4,6 @@ import com.fr.common.report.ReportState; import com.fr.design.DesignerEnvManager; import com.fr.design.RestartHelper; import com.fr.design.dialog.TipDialog; -import com.fr.design.fun.DesignerPortProvider; import com.fr.design.gui.ibutton.UIButton; import com.fr.design.gui.ilable.UILabel; import com.fr.design.gui.itextfield.UITextField; @@ -18,16 +17,20 @@ import com.fr.general.IOUtils; import com.fr.process.engine.core.CarryMessageEvent; import com.fr.process.engine.core.FineProcessContext; import com.fr.stable.StringUtils; -import com.fr.stable.bridge.StableFactory; import com.fr.stable.xml.XMLPrintWriter; import com.fr.stable.xml.XMLReadable; import com.fr.stable.xml.XMLWriter; import com.fr.stable.xml.XMLableReader; -import javax.swing.*; +import javax.swing.BorderFactory; +import javax.swing.JDialog; +import javax.swing.JPanel; import javax.swing.event.DocumentEvent; import javax.swing.event.DocumentListener; -import java.awt.*; +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.Dimension; +import java.awt.FlowLayout; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; diff --git a/designer-base/src/main/java/com/fr/design/utils/LinkStrUtils.java b/designer-base/src/main/java/com/fr/design/utils/LinkStrUtils.java index b7f8217b4..2bb245b44 100644 --- a/designer-base/src/main/java/com/fr/design/utils/LinkStrUtils.java +++ b/designer-base/src/main/java/com/fr/design/utils/LinkStrUtils.java @@ -42,6 +42,10 @@ public class LinkStrUtils { return "" + text + ""; } + + public static String generateLinkTagWithoutUnderLine(String link, String text) { + return "" + text + ""; + } public static String generateStyle(Color backgroundColor, Font font, Color fontColor) { diff --git a/designer-base/src/main/java/com/fr/design/utils/TemplateUtils.java b/designer-base/src/main/java/com/fr/design/utils/TemplateUtils.java index c4cb64db0..9fb1e632a 100644 --- a/designer-base/src/main/java/com/fr/design/utils/TemplateUtils.java +++ b/designer-base/src/main/java/com/fr/design/utils/TemplateUtils.java @@ -3,12 +3,14 @@ package com.fr.design.utils; import com.fr.base.extension.FileExtension; import com.fr.design.file.HistoryTemplateListCache; import com.fr.design.file.TemplateTreePane; +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.JTemplate; import com.fr.design.worker.save.SaveFailureHandler; import com.fr.file.FILE; import com.fr.file.FILEChooserPane; +import com.fr.file.filetree.FileNode; import com.fr.file.filter.ChooseFileFilter; import com.fr.log.FineLoggerFactory; import com.fr.stable.ArrayUtils; @@ -19,6 +21,8 @@ import com.fr.workspace.server.lock.TplOperator; import javax.swing.SwingWorker; import java.io.OutputStream; +import java.util.List; +import java.util.stream.Collectors; /** * @author hades @@ -134,4 +138,34 @@ public class TemplateUtils { } return name.substring(0, index) + Toolkit.i18nText("Fine_Design_Template_Has_Been_Locked") + name.substring(index); } + + /** + * 检测当前所选模板是否已经被打开 + * + * @return + */ + public static boolean checkSelectedTemplateIsEditing() { + return checkTemplateIsEditing(TemplateTreePane.getInstance().getTemplateFileTree().getSelectedTreeNodes()); + } + + /** + * 检测指定模板节点是否已经被打开 + * + * @param treeNodes + * @return + */ + public static boolean checkTemplateIsEditing(ExpandMutableTreeNode[] treeNodes) { + if (ArrayUtils.isEmpty(treeNodes)) { + return false; + } + List> jTemplates = HistoryTemplateListCache.getInstance().getHistoryList(); + List openedFile = jTemplates.stream().map(JTemplate::getPath).collect(Collectors.toList()); + for (ExpandMutableTreeNode treeNode : treeNodes) { + String templatePath = ((FileNode) (treeNode.getUserObject())).getEnvPath(); + if (openedFile.contains(templatePath)) { + return true; + } + } + return false; + } } diff --git a/designer-base/src/main/java/com/fr/env/detect/ui/EnvDetectorAction.java b/designer-base/src/main/java/com/fr/env/detect/ui/EnvDetectorAction.java index 7992648f0..42ab6306a 100644 --- a/designer-base/src/main/java/com/fr/env/detect/ui/EnvDetectorAction.java +++ b/designer-base/src/main/java/com/fr/env/detect/ui/EnvDetectorAction.java @@ -15,7 +15,7 @@ public class EnvDetectorAction extends UpdateAction { public EnvDetectorAction() { - this.setName(Toolkit.i18nText("Fine-Design_Basic_Detect_Toolbar_Title")); + this.setName(Toolkit.i18nText("Fine-Design_Basic_Carton_Toolbox_Title")); this.setSmallIcon("com/fr/env/detect/detect_normal.svg"); } diff --git a/designer-base/src/main/java/com/fr/env/detect/ui/EnvDetectorDialog.java b/designer-base/src/main/java/com/fr/env/detect/ui/EnvDetectorDialog.java index ed03d78cc..48dfecd7b 100644 --- a/designer-base/src/main/java/com/fr/env/detect/ui/EnvDetectorDialog.java +++ b/designer-base/src/main/java/com/fr/env/detect/ui/EnvDetectorDialog.java @@ -6,12 +6,14 @@ import com.fr.design.components.notification.NotificationDialogProperties; import com.fr.design.components.notification.NotificationModel; import com.fr.design.components.table.TablePanel; import com.fr.design.constants.DesignerColor; +import com.fr.design.constants.UIConstants; import com.fr.design.gui.ibutton.UIButton; import com.fr.design.gui.ibutton.UIButtonUI; import com.fr.design.gui.icheckbox.UICheckBox; import com.fr.design.gui.ilable.UILabel; import com.fr.design.i18n.Toolkit; import com.fr.design.layout.FRGUIPaneFactory; +import com.fr.design.mainframe.DesignerContext; import com.fr.design.ui.util.UIUtil; import com.fr.design.utils.gui.GUICoreUtils; import com.fr.design.utils.gui.GUIPaintUtils; @@ -22,6 +24,9 @@ import com.fr.env.detect.bean.DetectorResult; import com.fr.env.detect.bean.DetectorStatus; import com.fr.env.detect.bean.DetectorType; import com.fr.log.FineLoggerFactory; +import com.fr.design.carton.FeedbackToolboxDialog; +import com.fr.stable.ProductConstantsBase; +import com.fr.stable.StableUtils; import org.jetbrains.annotations.NotNull; import javax.swing.BorderFactory; @@ -37,8 +42,10 @@ import java.awt.Dimension; import java.awt.Frame; import java.awt.Graphics2D; import java.awt.Image; +import java.awt.Cursor; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; +import java.io.File; import java.net.URL; import java.util.List; import java.util.Map; @@ -51,93 +58,93 @@ import java.util.stream.Stream; * created by Harrison on 2022/05/26 **/ public class EnvDetectorDialog extends JDialog { - + private static final ImageIcon LOADING_ICON = getLoadingIcon(); private static final int TIMEOUT = 1000; - + private static final Color SUCCESS_COLOR = new Color(22, 193, 83); private static final Color DETAIL_FONT_COLOR = new Color(65, 155, 249); - + private final JPanel body; - + private final JPanel headerPanel; private UIButton detectButton; private JPanel resultSummaryPane; - + private final TablePanel tablePanel; - + private final JPanel tailPanel; - + /* 数据 model */ - + private EnvDetectorModel model; - + /* 流程 model */ - + /** * 默认是第一个要检测 */ private int currentDetectIndex = 0; - + private EnvDetectorButtonStatus buttonStatus = EnvDetectorButtonStatus.START; - + private SwingWorker detectWorker = null; - + /* config model */ - + private boolean detectOpen = EnvDetectorConfig.getInstance().isEnabled(); - + public EnvDetectorDialog(Frame owner) { this(owner, null); } - + public EnvDetectorDialog(Frame owner, EnvDetectorModel model) { super(owner); - + configProperties(); - + if (model == null) { this.model = new EnvDetectorModel(); } else { this.model = model; } - + this.body = FRGUIPaneFactory.createBorderLayout_L_Pane(); Color backgroundColor = new Color(240, 240, 243, 1); this.body.setBackground( backgroundColor); - + this.headerPanel = createHeaderPanel(); body.add(headerPanel, BorderLayout.NORTH); - + this.tablePanel = createTablePanel(); body.add(tablePanel, BorderLayout.CENTER); - + /* tailPanel*/ this.tailPanel = createTailPanel(); body.add(tailPanel, BorderLayout.SOUTH); - + add(body); - + + Dimension preferredSize = body.getPreferredSize(); setSize(preferredSize); - + repaint(); pack(); - GUICoreUtils.centerWindow(this); } - + /* header */ - + @NotNull private JPanel createHeaderPanel() { - + JPanel headerPanel = FRGUIPaneFactory.createBorderLayout_S_Pane(); headerPanel.setBorder(BorderFactory.createEmptyBorder(5, 0, 12, 0)); this.detectButton = new UIButton(buttonStatus.getDesc()) { @Override public ButtonUI getUI() { - + return new UIButtonUI() { @Override protected void doExtraPainting(UIButton b, Graphics2D g2d, int w, int h, String selectedRoles) { @@ -167,12 +174,32 @@ public class EnvDetectorDialog extends JDialog { detectButton.setBorderPainted(false); detectButton.setContentAreaFilled(false); headerPanel.add(detectButton, BorderLayout.WEST); - + + UILabel openUtilBoxLabel = new UILabel(Toolkit.i18nText("Fine-Design_Basic_Carton_Feedback_ToolBox")); + openUtilBoxLabel.setForeground(UIConstants.FLESH_BLUE); + + openUtilBoxLabel.addMouseListener(new MouseAdapter() { + @Override + public void mousePressed(MouseEvent e) { + setVisible(false); + FeedbackToolboxDialog myDialog = new FeedbackToolboxDialog(DesignerContext.getDesignerFrame()); + myDialog.setVisible(true); + dispose(); + } + public void mouseEntered(MouseEvent evt) { + Object source = evt.getSource(); + + if (source instanceof UILabel) { + ((UILabel) source).setCursor(new Cursor(Cursor.HAND_CURSOR)); + } + } + }); + headerPanel.add(openUtilBoxLabel, BorderLayout.EAST); return headerPanel; } - + private void startDetecting() { - + // 重新检测的时候需要处理一些逻辑 if (buttonStatus == EnvDetectorButtonStatus.A_NEW) { reInit(); @@ -181,31 +208,31 @@ public class EnvDetectorDialog extends JDialog { buttonStatus = buttonStatus.next(); UIUtil.invokeLaterIfNeeded(EnvDetectorDialog.this::refreshHeader); detectWorker = new SwingWorker() { - + @Override - protected Void doInBackground() throws Exception { + protected Void doInBackground() { List items = model.getItems(); // 执行刷新 for (int i = currentDetectIndex; i < items.size(); i++) { - + // 看一下是否关闭了, 有可能已经关闭了。 if (buttonStatus.isNotExecuting()) { return null; } - + // 刷新一下面板-开始执行啦 UIUtil.invokeLaterIfNeeded(EnvDetectorDialog.this::refreshBody); - + EnvDetectorItem item = items.get(i); DetectorType type = item.getType(); - + // 执行检测, UI-当前在检测中 DetectorResult result = UIUtil.waitUntil( () -> DetectorBridge.getInstance().detect(type), TIMEOUT, TimeUnit.MILLISECONDS); // 获取结果 item.setResult(result); - + // 更新UI // 只有还在运行中,才会真正的刷新面板 if (buttonStatus.isExecuting()) { @@ -213,14 +240,14 @@ public class EnvDetectorDialog extends JDialog { UIUtil.invokeLaterIfNeeded(EnvDetectorDialog.this::refreshBody); currentDetectIndex++; } - + } return null; } - + @Override protected void done() { - + try { this.get(); if (buttonStatus.isExecuting()) { @@ -236,7 +263,7 @@ public class EnvDetectorDialog extends JDialog { // 开始执行 detectWorker.execute(); } - + private void reInit() { currentDetectIndex = 0; for (EnvDetectorItem e : model.getItems()) { @@ -245,11 +272,11 @@ public class EnvDetectorDialog extends JDialog { // 刷新一下面板-开始执行啦 UIUtil.invokeLaterIfNeeded(EnvDetectorDialog.this::refreshBody); } - + private void stopDetecting(UIButton detectButton) { - + buttonStatus = buttonStatus.next(); - + // 先停止 detectWorker.cancel(false); // 更改-UI @@ -261,9 +288,9 @@ public class EnvDetectorDialog extends JDialog { refreshBody(); }); } - + private void updateHeaderPanel() { - + // 刷新按钮 detectButton.setText(buttonStatus.getDesc()); if (buttonStatus == EnvDetectorButtonStatus.A_NEW) { @@ -278,7 +305,7 @@ public class EnvDetectorDialog extends JDialog { return Boolean.FALSE; }).reduce((a, b) -> a && b) .orElse(Boolean.FALSE); - + if (success) { resultSummaryPane.add(new UILabel(Toolkit.i18nText("Fine-Design_Basic_Detect_Result_Label")), BorderLayout.WEST); UILabel successLabel = new UILabel(Toolkit.i18nText("Fine-Design_Basic_Detect_Result_Success")); @@ -297,32 +324,32 @@ public class EnvDetectorDialog extends JDialog { } } } - + /* table */ - - + + @NotNull private TablePanel createTablePanel() { - + TablePanel tablePanel = new TablePanel(18, 3); tablePanel.updateHeaders(new String[] { Toolkit.i18nText("Fine-Design_Basic_Detect_Kind"), Toolkit.i18nText("Fine-Design_Basic_Detect_Item"), Toolkit.i18nText("Fine-Design_Basic_Detect_Result")}); - + updateTable(tablePanel); - + return tablePanel; } - + private void updateTable(TablePanel tablePanel) { - + Map> itemMap = model.getItemMap(); - + // 行号, 这边更新是通过 行/列 。 不是索引 int row = 1; for (Map.Entry> entry : itemMap.entrySet()) { - + DetectorType.Kind kind = entry.getKey(); List items = entry.getValue(); for (int i = 0; i < items.size(); i++) { @@ -332,9 +359,9 @@ public class EnvDetectorDialog extends JDialog { EnvDetectorItem item = items.get(i); tablePanel.updateCell(row, 2, new UILabel(item.getDescription())); DetectorResult result = item.getResult(); - + int detectRow = currentDetectIndex + 1; - + if (result == null) { // 处于非正在检测状态 或者 索引不等于当前行号的时候 UILabel label; @@ -353,9 +380,9 @@ public class EnvDetectorDialog extends JDialog { } } } - + private Component createResultComponent(DetectorResult result) { - + JPanel statusPanel = FRGUIPaneFactory.createBorderLayout_S_Pane(); if (result.getStatus() == DetectorStatus.NORMAL) { statusPanel.add(new UILabel(IconUtils.readIcon("/com/fr/design/standard/reminder/reminder_success.svg")), BorderLayout.WEST); @@ -367,7 +394,7 @@ public class EnvDetectorDialog extends JDialog { infoPanel.add(new UILabel(Toolkit.i18nText("Fine-Design_Basic_Exception")), BorderLayout.CENTER); } statusPanel.add(infoPanel, BorderLayout.WEST); - + // 如果结果是检测出的异常,则出现详细信息。 if (result.getStatus() == DetectorStatus.EXCEPTION) { JPanel detailPanel = FRGUIPaneFactory.createBorderLayout_S_Pane(); @@ -385,7 +412,7 @@ public class EnvDetectorDialog extends JDialog { .filter((e) -> e.getStatus() == DetectorStatus.EXCEPTION) .map(DetectorUtil::convert2Notification) .collect(Collectors.toList()); - + NotificationDialog dialog = new NotificationDialog(properties, notificationModels); dialog.open(); } @@ -394,19 +421,19 @@ public class EnvDetectorDialog extends JDialog { } statusPanel.add(detailPanel, BorderLayout.CENTER); } - + } return statusPanel; } - + /* tail */ @NotNull private JPanel createTailPanel() { - + JPanel tailPanel = FRGUIPaneFactory.createBorderLayout_S_Pane(); tailPanel.setBorder(BorderFactory.createEmptyBorder(20, 0, 0, 0)); - + JPanel configPanel = FRGUIPaneFactory.createBorderLayout_S_Pane(); { UICheckBox checkBox = new UICheckBox(); @@ -420,7 +447,7 @@ public class EnvDetectorDialog extends JDialog { configPanel.add(description, BorderLayout.EAST); } tailPanel.add(configPanel, BorderLayout.WEST); - + JPanel actionsPanel = FRGUIPaneFactory.createBorderLayout_S_Pane(); actionsPanel.setLayout(FRGUIPaneFactory.createM_BorderLayout()); { @@ -432,7 +459,7 @@ public class EnvDetectorDialog extends JDialog { EnvDetectorConfig.getInstance().setEnabled(this.detectOpen); }); actionsPanel.add(confirmButton, BorderLayout.WEST); - + UIButton cancelButton = new UIButton(Toolkit.i18nText("Fine-Design_Basic_Cancel")); cancelButton.addActionListener((e) -> { setVisible(false); @@ -443,23 +470,23 @@ public class EnvDetectorDialog extends JDialog { tailPanel.add(actionsPanel, BorderLayout.EAST); return tailPanel; } - + private void refreshHeader() { - + updateHeaderPanel(); pack(); repaint(); } - + private void refreshBody() { - + updateTable(this.tablePanel); pack(); repaint(); } - + private static ImageIcon getLoadingIcon() { - + URL resource = EnvDetectorDialog.class.getResource("/com/fr/design/standard/loading/loading-120.gif"); if (resource == null) { return null; @@ -470,21 +497,21 @@ public class EnvDetectorDialog extends JDialog { loadingIcon.setImage(loadingIcon.getImage().getScaledInstance(width, height, Image.SCALE_DEFAULT)); return loadingIcon; } - + private void configProperties() { - + setTitle(Toolkit.i18nText("Fine-Design_Basic_Detect_Title")); setModal(false); setFocusable(false); setAutoRequestFocus(false); setResizable(false); } - + /** * 按钮的当前状态 */ private enum EnvDetectorButtonStatus { - + /** * 开始 -> 停止 */ @@ -494,7 +521,7 @@ public class EnvDetectorDialog extends JDialog { return STOP; } }, - + /** * 停止 -> 继续 */ @@ -504,7 +531,7 @@ public class EnvDetectorDialog extends JDialog { return CONTINUE; } }, - + /** * 继续 -> 停止 */ @@ -514,7 +541,7 @@ public class EnvDetectorDialog extends JDialog { return STOP; } }, - + /** * 重新 -> 停止 */ @@ -524,46 +551,46 @@ public class EnvDetectorDialog extends JDialog { return STOP; } } - + ; - + private String descLocale; - + EnvDetectorButtonStatus(String descLocale) { - + this.descLocale = descLocale; } - + public String getDesc() { - + return Toolkit.i18nText(descLocale); } - + /** * 在执行中 * * @return 是/否 */ public boolean isExecuting() { - + return this == EnvDetectorButtonStatus.STOP; }; - + /** * 不在执行中 * * @return 是/否 */ public boolean isNotExecuting() { - + return !isExecuting(); } - + public abstract EnvDetectorButtonStatus next(); } - + private class EnvDetectorHeaderPanel extends JPanel { - - + + } } 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/java/com/fr/start/BaseDesigner.java b/designer-base/src/main/java/com/fr/start/BaseDesigner.java index 89c928e2c..7dadc636f 100644 --- a/designer-base/src/main/java/com/fr/start/BaseDesigner.java +++ b/designer-base/src/main/java/com/fr/start/BaseDesigner.java @@ -35,9 +35,11 @@ import com.fr.start.common.DesignerStartupContext; import com.fr.start.common.DesignerStartupUtil; import com.fr.start.event.LazyStartupEvent; import com.fr.workspace.base.WorkspaceStatus; +import org.jetbrains.annotations.Nullable; import java.awt.Window; import java.lang.reflect.Method; +import java.util.concurrent.atomic.AtomicBoolean; /** * The main class of Report Designer. @@ -125,7 +127,7 @@ public abstract class BaseDesigner extends ToolBarMenuDock { if (args != null && args.length > 0) { file = DesignerStartupUtil.convertArgs2FILE(args); } else { - file = FILEFactory.createFILE(FILEFactory.ENV_PREFIX + DesignerEnvManager.getEnvManager().getLastOpenFile()); + file = getLastOpenFile(); } DesignerFrame df = DesignerContext.getDesignerFrame(); isException = openFile(df, isException, file); @@ -140,9 +142,58 @@ public abstract class BaseDesigner extends ToolBarMenuDock { } } } - + + @Nullable + private FILE getLastOpenFile() { + + FILE file = DesignerStartupContext.getInstance().getStartingTemplateFile(); + if (file == null) { + file = FILEFactory.createFILE(FILEFactory.ENV_PREFIX + DesignerEnvManager.getEnvManager().getLastOpenFile()); + } + return file; + } + private boolean openFile(final DesignerFrame df, boolean isException, FILE file) { - + + AtomicBoolean isExWrapper = new AtomicBoolean(isException); + openTemplate(df, isExWrapper, file); + + if (OperatingSystem.isMacOS()) { + enableFullScreenMode(df); + } + + JTemplate selectedJTemplate = df.getSelectedJTemplate(); + if (selectedJTemplate != null) { + selectedJTemplate.requestGridFocus(); + } + return isExWrapper.get(); + } + + private void openTemplate(DesignerFrame df, AtomicBoolean isException, FILE file) { + + // 如果是起始页启动中 + if (openTemplateOnStartup(df, isException, file)) { + return; + } + + openTemplate0(df, isException, file); + } + + private void openTemplate0(DesignerFrame df, AtomicBoolean isException, FILE file) { + + file = getExtraFILE(isException, file); + + if (file != null && file.exists() && !isException.get()) { + df.openTemplate(file); + } else { + df.addAndActivateJTemplate(); + // 如果没有模板,则需要确认一下 + MutilTempalteTabPane.getInstance().setTemTemplate(HistoryTemplateListPane.getInstance().getCurrentEditingTemplate()); + } + } + + private FILE getExtraFILE(AtomicBoolean isException, FILE file) { + //启动时打开指定文件的接口 DesignerStartOpenFileProcessor processor = ExtraDesignClassManager.getInstance().getSingle(DesignerStartOpenFileProcessor.XML_TAG); // 如果插件没有,且又开启了启动时打开空文件,则使用启动时打开空文件 @@ -154,53 +205,50 @@ public abstract class BaseDesigner extends ToolBarMenuDock { if (f != null) { file = f;//避免null } else { - isException = true;//此时有文件nullpointer异常,执行打开空文件 + isException.set(true);//此时有文件nullpointer异常,执行打开空文件 } } - - openTemplate(df, isException, file); - - if (OperatingSystem.isMacOS()) { - enableFullScreenMode(df); - } - - JTemplate selectedJTemplate = df.getSelectedJTemplate(); - if (selectedJTemplate != null) { - selectedJTemplate.requestGridFocus(); - } - return isException; + return file; } - private void openTemplate(DesignerFrame df, boolean isException, FILE file) { + private boolean openTemplateOnStartup(DesignerFrame df, AtomicBoolean isException, FILE file) { boolean onStartup = DesignerStartupContext.getInstance().isSupport(); if (onStartup) { DesignerStartupContext context = DesignerStartupContext.getInstance(); if (context.isCreateNew()) { - df.addAndActivateJTemplate(); - // 如果没有模板,则需要确认一下 - MutilTempalteTabPane.getInstance().setTemTemplate(HistoryTemplateListPane.getInstance().getCurrentEditingTemplate()); - return; + return createNewTemplate(df); } - if (context.isOpenLastFile()) { - if (file != null && file.exists() && !isException) { - df.openTemplate(file); - return; - } + if (isOpenTemplate(isException, file, context)) { + return openTemplate(df, file); } if (context.isOpenEmpty()) { - df.showEmptyJTemplate(); - return; + return openEmpty(df); } } + return false; + } - if (file != null && file.exists() && !isException) { - df.openTemplate(file); - } else { - df.addAndActivateJTemplate(); - // 如果没有模板,则需要确认一下 - MutilTempalteTabPane.getInstance().setTemTemplate(HistoryTemplateListPane.getInstance().getCurrentEditingTemplate()); - } + private boolean isOpenTemplate(AtomicBoolean isException, FILE file, DesignerStartupContext context) { + + return context.isOpenLastFile() && file != null && file.exists() && !isException.get(); + } + + private boolean openEmpty(DesignerFrame df) { + df.showEmptyJTemplate(); + return true; + } + + private boolean openTemplate(DesignerFrame df, FILE file) { + df.openTemplate(file); + return true; + } + + private boolean createNewTemplate(DesignerFrame df) { + df.addAndActivateJTemplate(); + // 如果没有模板,则需要确认一下 + MutilTempalteTabPane.getInstance().setTemTemplate(HistoryTemplateListPane.getInstance().getCurrentEditingTemplate()); + return true; } private void enableFullScreenMode(Window window) { diff --git a/designer-base/src/main/java/com/fr/start/common/DesignerOpenEmptyPanel.java b/designer-base/src/main/java/com/fr/start/common/DesignerOpenEmptyPanel.java index e89598ae2..7f4a0eeb9 100644 --- a/designer-base/src/main/java/com/fr/start/common/DesignerOpenEmptyPanel.java +++ b/designer-base/src/main/java/com/fr/start/common/DesignerOpenEmptyPanel.java @@ -1,6 +1,7 @@ package com.fr.start.common; import com.fr.base.svg.IconUtils; +import com.fr.design.file.HistoryTemplateListCache; import com.fr.design.file.HistoryTemplateListPane; import com.fr.design.file.MutilTempalteTabPane; import com.fr.design.gui.ilable.UILabel; @@ -55,6 +56,7 @@ public class DesignerOpenEmptyPanel extends JPanel { @Override public void actionPerformed(ActionEvent e) { DesignerFrame df = DesignerContext.getDesignerFrame(); + HistoryTemplateListCache.getInstance().setCurrentEditingTemplate(null); df.addAndActivateJTemplate(); // 如果没有模板,则需要确认一下 MutilTempalteTabPane.getInstance().setTemTemplate(HistoryTemplateListPane.getInstance().getCurrentEditingTemplate()); @@ -68,7 +70,7 @@ public class DesignerOpenEmptyPanel extends JPanel { this.body.add(createIcon, BorderLayout.NORTH); this.body.add(createButtonPanel, BorderLayout.SOUTH); - setLayout(FRGUIPaneFactory.createCenterLayout(this.body)); + setLayout(FRGUIPaneFactory.createCenterLayout(this.body, 0.4d, 0.4d)); ColorUtils.syncBackground(this, Color.WHITE); diff --git a/designer-base/src/main/java/com/fr/start/common/DesignerStartupConfig.java b/designer-base/src/main/java/com/fr/start/common/DesignerStartupConfig.java index 5aae98f03..6afef4f3b 100644 --- a/designer-base/src/main/java/com/fr/start/common/DesignerStartupConfig.java +++ b/designer-base/src/main/java/com/fr/start/common/DesignerStartupConfig.java @@ -12,6 +12,11 @@ public class DesignerStartupConfig implements XMLable { private static final DesignerStartupConfig INSTANCE = new DesignerStartupConfig(); + /** + * 加上版本,不然回滚到 1107 会有兼容问题 + */ + private static final String TAG_ENABLED = "isEnabled1108"; + public static DesignerStartupConfig getInstance() { return INSTANCE; @@ -20,7 +25,7 @@ public class DesignerStartupConfig implements XMLable { /** * 默认值是 false */ - private boolean enabled = false; + private boolean enabled = true; public boolean isEnabled() { return enabled; @@ -30,25 +35,24 @@ public class DesignerStartupConfig implements XMLable { this.enabled = enabled; } - @Override public Object clone() throws CloneNotSupportedException { DesignerStartupConfig config = new DesignerStartupConfig(); - config.setEnabled(true); + config.setEnabled(enabled); return config; } @Override public void readXML(XMLableReader reader) { if (reader.isAttr()) { - this.setEnabled(reader.getAttrAsBoolean("isEnabled", false)); + this.setEnabled(reader.getAttrAsBoolean(TAG_ENABLED, true)); } } @Override public void writeXML(XMLPrintWriter writer) { writer.startTAG(XML_TAG); - writer.attr("isEnabled", this.isEnabled()); + writer.attr(TAG_ENABLED, this.isEnabled()); writer.end(); } diff --git a/designer-base/src/main/java/com/fr/start/common/DesignerStartupContext.java b/designer-base/src/main/java/com/fr/start/common/DesignerStartupContext.java index b996fbe4b..b42a6c65b 100644 --- a/designer-base/src/main/java/com/fr/start/common/DesignerStartupContext.java +++ b/designer-base/src/main/java/com/fr/start/common/DesignerStartupContext.java @@ -3,7 +3,9 @@ package com.fr.start.common; import com.fr.design.DesignerEnvManager; import com.fr.design.env.DesignerWorkspaceInfo; import com.fr.design.env.DesignerWorkspaceType; +import com.fr.file.FileFILE; import com.fr.start.module.StartupArgs; +import com.fr.startup.metric.DesignerMetrics; import com.fr.startup.ui.StartupPageModel; import com.fr.third.guava.collect.Lists; import com.fr.third.org.apache.commons.lang3.time.StopWatch; @@ -18,6 +20,12 @@ import java.util.Iterator; **/ public class DesignerStartupContext { + /** + * 可以启动 + * 当遇到 mac 双击启动时,需要将这里置为 false, 见 {@link FileOpen4MacDeepLink} + */ + private boolean enabled = true; + /** * 启动参数 */ @@ -28,6 +36,11 @@ public class DesignerStartupContext { */ private StartupPageModel startupPageModel; + /** + * 设计器启动埋点 + */ + private final DesignerMetrics designerMetrics = new DesignerMetrics(); + /** * 是否在起始页打开的等待过程中 */ @@ -58,6 +71,11 @@ public class DesignerStartupContext { */ private boolean createNew; + /** + * 启动的模板 + */ + private FileFILE startingTemplateFile; + /** * 时间记录 */ @@ -66,7 +84,15 @@ public class DesignerStartupContext { public static DesignerStartupContext getInstance() { return StartupContextHolder.INSTANCE; } - + + public boolean isEnabled() { + return enabled; + } + + public void setEnabled(boolean enabled) { + this.enabled = enabled; + } + private static class StartupContextHolder { private static final DesignerStartupContext INSTANCE = new DesignerStartupContext(); } @@ -75,11 +101,23 @@ public class DesignerStartupContext { return STOP_WATCH; } + public DesignerMetrics getDesignerMetrics() { + return designerMetrics; + } + /* 启动模式 */ + public FileFILE getStartingTemplateFile() { + return startingTemplateFile; + } + + public void setStartingTemplateFile(FileFILE startingTemplateFile) { + this.startingTemplateFile = startingTemplateFile; + } + /** * 展示启动页 - * 1. 判断当前的工作目录数量 + * 1. 判断当前的工作目录数量为空或者为1 * 2. 判断是否是 demo、还是打开目标文件 * 3. 功能是否开启 * @@ -87,10 +125,20 @@ public class DesignerStartupContext { */ public boolean isShowStartupPage() { + DesignerEnvManager envManager = DesignerEnvManager.getEnvManager(); + return !startupArgs.isDemo() && DesignerStartupUtil.convertArgs2FILE(startupArgs.get()) == null + // 见该 field 的注释 + && enabled + && isWorkspaceValid() + && envManager.isStartupPageEnabled(); + } + + private boolean isWorkspaceValid() { + DesignerEnvManager envManager = DesignerEnvManager.getEnvManager(); Iterator envNameIterator = envManager.getEnvNameIterator(); ArrayList envs = Lists.newArrayList(envNameIterator); - return !startupArgs.isDemo() && DesignerStartupUtil.convertArgs2FILE(startupArgs.get()) == null && !envs.isEmpty() && envManager.isStartupPageEnabled(); + return !envs.isEmpty() && (envs.size() != 1); } /* 预热相关 */ @@ -104,7 +152,12 @@ public class DesignerStartupContext { String curEnvName = DesignerEnvManager.getEnvManager().getCurEnvName(); DesignerWorkspaceInfo workspaceInfo = DesignerEnvManager.getEnvManager().getWorkspaceInfo(curEnvName); - return workspaceInfo.getType() == DesignerWorkspaceType.Local; + boolean valid = false; + try { + valid = workspaceInfo.checkValid(); + } catch (Exception ignore) { + } + return workspaceInfo.getType() == DesignerWorkspaceType.Local && valid; } public void setStartupPageModel(StartupPageModel startupPageModel) { diff --git a/designer-base/src/main/java/com/fr/start/common/DesignerStartupExecutor.java b/designer-base/src/main/java/com/fr/start/common/DesignerStartupExecutor.java index 1a033e8cd..b4358f047 100644 --- a/designer-base/src/main/java/com/fr/start/common/DesignerStartupExecutor.java +++ b/designer-base/src/main/java/com/fr/start/common/DesignerStartupExecutor.java @@ -1,15 +1,10 @@ package com.fr.start.common; -import java.util.ArrayList; -import java.util.List; - /** * created by Harrison on 2022/07/03 **/ public class DesignerStartupExecutor { - private List warmupTasks = new ArrayList<>(); - public void execute(Runnable runnable) { if (!DesignerStartupContext.getInstance().onWarmup()) { @@ -17,11 +12,6 @@ public class DesignerStartupExecutor { } } - public void reset() { - - warmupTasks.clear(); - } - public static DesignerStartupExecutor getInstance() { return DesignerStartupExecutorHolder.INSTANCE; } diff --git a/designer-base/src/main/java/com/fr/start/common/DesignerStartupUtil.java b/designer-base/src/main/java/com/fr/start/common/DesignerStartupUtil.java index 5bc55e80c..1bcf8be95 100644 --- a/designer-base/src/main/java/com/fr/start/common/DesignerStartupUtil.java +++ b/designer-base/src/main/java/com/fr/start/common/DesignerStartupUtil.java @@ -6,16 +6,44 @@ import com.fr.file.FILE; import com.fr.file.FILEFactory; import com.fr.file.FileFILE; import com.fr.general.ComparatorUtils; +import com.fr.startup.ui.StartupPageModel; import org.jetbrains.annotations.Nullable; import java.io.File; +import java.util.Optional; /** * created by Harrison on 2022/07/09 **/ public class DesignerStartupUtil { - @Nullable + /** + * 如果是在启动页中 + * + * @param file 文件 + * @return 成功/失败 + */ + public static boolean openTemplateIfOnWaiting(File file) { + + DesignerStartupContext context = DesignerStartupContext.getInstance(); + // 如果在启动页展示中 + if (context.isOnWaiting()) { + FileFILE fileFILE = new FileFILE(file); + // 设置上一次启动模板为当前模板 + DesignerStartupContext.getInstance().setStartingTemplateFile(fileFILE); + StartupPageModel model = context.getStartupPageModel(); + Optional.ofNullable(model) + .ifPresent((e) -> { + // 执行上一次模板的启动 + Runnable openLastTemplateRunnable = e.getOpenLastTemplateRunnable(); + openLastTemplateRunnable.run(); + }); + return true; + } + return false; + } + + @Nullable public static FILE convertArgs2FILE(String[] args) { // p:需要打开这个报表文件,这个代码不能删除. diff --git a/designer-base/src/main/java/com/fr/startup/metric/DesignerMetrics.java b/designer-base/src/main/java/com/fr/startup/metric/DesignerMetrics.java new file mode 100644 index 000000000..aa7391df7 --- /dev/null +++ b/designer-base/src/main/java/com/fr/startup/metric/DesignerMetrics.java @@ -0,0 +1,23 @@ +package com.fr.startup.metric; + +/** + * created by Harrison on 2022/08/12 + **/ +public class DesignerMetrics { + + private DesignerStartupModel model = new DesignerStartupModel(); + + private DesignerStartupPageStatistic statistic = new DesignerStartupPageStatistic(); + + public DesignerMetrics() { + } + + public DesignerStartupModel getModel() { + return model; + } + + public DesignerStartupPageStatistic getStatistic() { + return statistic; + } + +} diff --git a/designer-base/src/main/java/com/fr/startup/metric/DesignerStartupModel.java b/designer-base/src/main/java/com/fr/startup/metric/DesignerStartupModel.java new file mode 100644 index 000000000..9c0c07315 --- /dev/null +++ b/designer-base/src/main/java/com/fr/startup/metric/DesignerStartupModel.java @@ -0,0 +1,193 @@ +package com.fr.startup.metric; + +import com.fr.json.JSONArray; +import com.fr.json.JSONObject; +import com.fr.plugin.context.PluginContext; +import com.fr.plugin.manage.PluginManager; +import com.fr.stable.os.AbstractOperatingSystem; +import com.fr.stable.os.OperatingSystem; +import com.fr.start.common.DesignerStartupConfig; +import com.fr.workspace.WorkContext; + +import java.lang.management.ManagementFactory; +import java.util.List; +import java.util.stream.Collectors; + +/** + * 设计器启动数据 + * + * created by Harrison on 2022/08/12 + **/ +public class DesignerStartupModel { + + /** + * landingTime:用户从双击图标/.bat启动等,到出现起始页的时间 + */ + private long landingTime; + + /** + * startingTime:用户从起始页进入设计器,完全可用的时间 + */ + private long startingTime; + + /** + * info:设计器环境的详细信息。记录环境信息、机器信息、远程or本地、插件信息。 + */ + private MachineInfo info; + + /** + * mode:模式,0-有设计器起动页;1-无设计器起始页 + */ + private int mode; + + public DesignerStartupModel() { + } + + public DesignerStartupModel(long landingTime, long startingTime, MachineInfo info, int mode) { + this.landingTime = landingTime; + this.startingTime = startingTime; + this.info = info; + this.mode = mode; + } + + public long getLandingTime() { + return landingTime; + } + + public void setLandingTime(long landingTime) { + this.landingTime = landingTime; + } + + public long getStartingTime() { + return startingTime; + } + + public void setStartingTime(long startingTime) { + this.startingTime = startingTime; + } + + public MachineInfo getInfo() { + return info; + } + + public void setInfo(MachineInfo info) { + this.info = info; + } + + public int getMode() { + return mode; + } + + public void setMode(int mode) { + this.mode = mode; + } + + private void fillInfo() { + + MachineInfo info = new MachineInfo(); + AbstractOperatingSystem operatingSystem = OperatingSystem.getOperatingSystem(); + info.setSystem(operatingSystem.getDisplayString()); + + try { + final int byteToMb = 1024 * 1024; + com.sun.management.OperatingSystemMXBean operatingSystemMXBean = (com.sun.management.OperatingSystemMXBean) ManagementFactory.getOperatingSystemMXBean(); + long free = operatingSystemMXBean.getFreePhysicalMemorySize() / byteToMb; + long total = operatingSystemMXBean.getTotalPhysicalMemorySize() / byteToMb; + long used = total - free; + JSONObject jo = new JSONObject(); + jo.put("free", free); + jo.put("used", used); + jo.put("total", total); + info.setMachine(jo.toString()); + } catch (Exception ignored) { + } + + boolean local = WorkContext.getCurrent().isLocal(); + info.setWork(local ? 1 : 0); + + List contexts = PluginManager.getContexts(); + List contextNames = contexts.stream() + .map(PluginContext::getName) + .collect(Collectors.toList()); + JSONArray contextNameJa = new JSONArray(contextNames); + info.setPlugins(contextNameJa.toString()); + this.setInfo(info); + + } + + private void fillMode() { + + this.setMode(DesignerStartupConfig.getInstance().isEnabled() ? 0 : 1); + } + + public void fill() { + + fillInfo(); + fillMode(); + } + + private static class MachineInfo { + + /** + * 系统信息 + */ + private String system; + + /** + * 机器信息 + */ + private String machine; + + /** + * work:0-远程;1-本地; + */ + private int work; + + /** + * 插件列表 + */ + private String plugins; + + public MachineInfo() { + } + + public MachineInfo(String system, String machine, int work, String plugins) { + this.system = system; + this.machine = machine; + this.work = work; + this.plugins = plugins; + } + + public String getSystem() { + return system; + } + + public void setSystem(String system) { + this.system = system; + } + + public String getMachine() { + return machine; + } + + public void setMachine(String machine) { + this.machine = machine; + } + + public int getWork() { + return work; + } + + public void setWork(int work) { + this.work = work; + } + + public String getPlugins() { + return plugins; + } + + public void setPlugins(String plugins) { + this.plugins = plugins; + } + } +} diff --git a/designer-base/src/main/java/com/fr/startup/metric/DesignerStartupPageStatistic.java b/designer-base/src/main/java/com/fr/startup/metric/DesignerStartupPageStatistic.java new file mode 100644 index 000000000..b68ea7335 --- /dev/null +++ b/designer-base/src/main/java/com/fr/startup/metric/DesignerStartupPageStatistic.java @@ -0,0 +1,199 @@ +package com.fr.startup.metric; + +import com.fr.stable.StringUtils; +import com.fr.start.common.DesignerStartupContext; +import com.fr.startup.ui.StartupPageModel; +import com.fr.startup.ui.StartupWorkspaceBean; + +import java.util.ArrayDeque; +import java.util.Deque; + +/** + * 设计器启动页使用数据 + * + * created by Harrison on 2022/08/12 + **/ +public class DesignerStartupPageStatistic { + + private final Deque operations = new ArrayDeque<>(); + + /** + * 见 {@link OperationType} 的注释 + */ + public void recordOpenEmptyTemplate() { + + Operation operation = OperationType.DO_OPEN_EMPTY_TEMPLATE.create(); + StartupPageModel pageModel = DesignerStartupContext.getInstance().getStartupPageModel(); + operation.setWorkspace(pageModel.getSelectWorkspaceInfo().getName()); + operation.setWorkspaceNum(pageModel.getWorkspaceInfos().size()); + pushOperation(operation); + } + + /** + * 见 {@link OperationType} 的注释 + */ + public void recordSwitchWorkspace(StartupWorkspaceBean lastWorkspaceInfo, StartupWorkspaceBean currentWorkspace) { + + if (lastWorkspaceInfo != null && StringUtils.equals(lastWorkspaceInfo.getName(), currentWorkspace.getName())) { + return; + } + Operation operation = OperationType.DO_SWITCH_WORKSPACE.create(); + StartupPageModel pageModel = DesignerStartupContext.getInstance().getStartupPageModel(); + operation.setWorkspace(currentWorkspace.getName()); + operation.setWorkspaceNum(pageModel.getWorkspaceInfos().size()); + pushOperation(operation); + } + + /** + * 见 {@link OperationType} 的注释 + */ + public void recordShowAllAction() { + + Operation operation = OperationType.DO_SHOW_ALL_ACTION.create(); + StartupPageModel pageModel = DesignerStartupContext.getInstance().getStartupPageModel(); + operation.setWorkspaceNum(pageModel.getWorkspaceInfos().size()); + pushOperation(operation); + } + + /** + * 见 {@link OperationType} 的注释 + */ + public void recordOpenLastTemplate(String lastOpenFile) { + + Operation operation = OperationType.DO_OPEN_LAST_TEMPLATE_ACTION.create(); + StartupPageModel pageModel = DesignerStartupContext.getInstance().getStartupPageModel(); + operation.setWorkspaceNum(pageModel.getWorkspaceInfos().size()); + operation.setTemplate(lastOpenFile); + pushOperation(operation); + } + + /** + * 添加操作 + * + * @param operation 操作 + */ + public void pushOperation(Operation operation) { + + this.operations.push(operation); + } + + /** + * 获取操作 + * + * @return 操作 + */ + public Deque getOperations() { + + return this.operations; + } + + public enum OperationType { + + /** + * 双击工作目录进入 或 点击蓝色箭头进入 + */ + DO_OPEN_EMPTY_TEMPLATE(0), + + /** + * 切换其他工作目录 + */ + DO_SWITCH_WORKSPACE(1), + + /** + * 点击展开全部 + */ + DO_SHOW_ALL_ACTION(2), + + /** + * 点击工作目录中的模版直接打开 或 直接点击蓝色箭头进入 + */ + DO_OPEN_LAST_TEMPLATE_ACTION(3); + + private final int sign; + + OperationType(int sign) { + this.sign = sign; + } + + public int getSign() { + return sign; + } + + public Operation create() { + + Operation operation = new Operation(); + operation.setOperateType(this); + return operation; + } + } + + public static class Operation { + + /** + * operate:0-双击工作目录进入 或 点击蓝色箭头进入;1-切换其他工作目录;2-点击展开全部;3-点击工作目录中的模版直接打开 或 直接点击蓝色箭头进入 + */ + private int operate; + + /** + * workplace:工作目录名称,当operate为 0或1时记录 + */ + private String workspace; + + /** + * workplaceNumber:工作目录的个数,当operate为 0或1或2或3时记录 + */ + private int workspaceNum; + + /** + * template:模板名称,当operate为 3时记录 + */ + private String template; + + public Operation(int operate, String workspace, int workspaceNum, String template) { + this.operate = operate; + this.workspace = workspace; + this.workspaceNum = workspaceNum; + this.template = template; + } + + public Operation() { + } + + public int getOperate() { + return operate; + } + + public void setOperateType(OperationType operateType) { + this.operate = operateType.getSign(); + } + + public void setOperate(int operate) { + this.operate = operate; + } + + public String getWorkspace() { + return workspace; + } + + public void setWorkspace(String workspace) { + this.workspace = workspace; + } + + public int getWorkspaceNum() { + return workspaceNum; + } + + public void setWorkspaceNum(int workspaceNum) { + this.workspaceNum = workspaceNum; + } + + public String getTemplate() { + return template; + } + + public void setTemplate(String template) { + this.template = template; + } + } + +} diff --git a/designer-base/src/main/java/com/fr/startup/ui/StartupPageConstants.java b/designer-base/src/main/java/com/fr/startup/ui/StartupPageConstants.java index ae27e1a9e..8c414cecf 100644 --- a/designer-base/src/main/java/com/fr/startup/ui/StartupPageConstants.java +++ b/designer-base/src/main/java/com/fr/startup/ui/StartupPageConstants.java @@ -1,5 +1,7 @@ package com.fr.startup.ui; +import java.awt.Color; + /** * created by Harrison on 2022/07/07 **/ @@ -14,4 +16,14 @@ public class StartupPageConstants { * 内容宽度 */ public static final int CONTENT_WIDTH = 850; + + /** + * 边框的颜色 + */ + public static final Color BORDER_COLOR = Color.WHITE; + + /** + * 透明的颜色 + */ + public static final Color TRANSPARENT_COLOR = new Color(0, 0, 0, 0); } diff --git a/designer-base/src/main/java/com/fr/startup/ui/StartupPageModel.java b/designer-base/src/main/java/com/fr/startup/ui/StartupPageModel.java index 4b82545a0..a461ee33b 100644 --- a/designer-base/src/main/java/com/fr/startup/ui/StartupPageModel.java +++ b/designer-base/src/main/java/com/fr/startup/ui/StartupPageModel.java @@ -3,10 +3,12 @@ package com.fr.startup.ui; import com.fr.design.DesignerEnvManager; import com.fr.design.env.DesignerWorkspaceInfo; import com.fr.design.env.DesignerWorkspaceType; +import com.fr.stable.StringUtils; import com.fr.third.guava.collect.Lists; import com.fr.workspace.connect.WorkspaceConnectionInfo; import java.util.ArrayList; +import java.util.Comparator; import java.util.HashMap; import java.util.Iterator; import java.util.List; @@ -33,18 +35,23 @@ public class StartupPageModel { public static StartupPageModel create() { DesignerEnvManager envManager = DesignerEnvManager.getEnvManager(); + String curEnvName = envManager.getCurEnvName(); Iterator envNameIterator = envManager.getEnvNameIterator(); + + Comparator startupWorkspaceBeanComparator = convertComparator(curEnvName); List infos = Lists.newArrayList(envNameIterator) .stream() .map((e) -> { DesignerWorkspaceInfo workspaceInfo = envManager.getWorkspaceInfo(e); if (workspaceInfo.getType() == DesignerWorkspaceType.Remote) { WorkspaceConnectionInfo connection = workspaceInfo.getConnection(); - return new StartupWorkspaceBean(e, connection.getUrl(), workspaceInfo.getType()); + String remoteAddress = StartupPageUtil.getRemoteAddress(connection.getUrl()); + return new StartupWorkspaceBean(e, remoteAddress, workspaceInfo.getType()); } else { return new StartupWorkspaceBean(e, workspaceInfo.getPath(), workspaceInfo.getType()); } }) + .sorted(startupWorkspaceBeanComparator) .collect(Collectors.toList()); Map> recentFileMap = new HashMap<>(); for (StartupWorkspaceBean info : infos) { @@ -108,4 +115,17 @@ public class StartupPageModel { public void setOpenEmptyTemplateRunnable(Runnable openEmptyTemplateRunnable) { this.openEmptyTemplateRunnable = openEmptyTemplateRunnable; } + + private static Comparator convertComparator(String curEnvName) { + + return (o1, o2) -> { + if (StringUtils.equals(curEnvName, o1.getName())) { + return -1; + } + if (StringUtils.equals(curEnvName, o2.getName())) { + return 1; + } + return 0; + }; + } } diff --git a/designer-base/src/main/java/com/fr/startup/ui/StartupPageUtil.java b/designer-base/src/main/java/com/fr/startup/ui/StartupPageUtil.java index 896b42595..153aa4ce0 100644 --- a/designer-base/src/main/java/com/fr/startup/ui/StartupPageUtil.java +++ b/designer-base/src/main/java/com/fr/startup/ui/StartupPageUtil.java @@ -2,14 +2,22 @@ package com.fr.startup.ui; import com.fr.base.svg.SVGIcon; import com.fr.design.env.DesignerWorkspaceType; +import com.fr.log.FineLoggerFactory; +import com.fr.stable.StringUtils; import javax.swing.Icon; +import javax.swing.JComponent; +import javax.swing.JRootPane; +import java.net.URL; /** * created by Harrison on 2022/07/11 **/ public class StartupPageUtil { + public static final int INVALID_PORT = -1; + public static final String COLON = ":"; + /** * 获取最近区域的 ICON * @@ -37,4 +45,42 @@ public class StartupPageUtil { } return SVGIcon.readSVGIcon("/com/fr/design/startup/remote_server_background_28.svg", 28, 28); } + + /** + * 返回 ip : port + * + * @param urlStr 完整的 url 值,例如 https://localhost:3090/xxx + * @return localhost:3090 + */ + public static String getRemoteAddress(String urlStr) { + + try { + if (StringUtils.isEmpty(urlStr)) { + return StringUtils.EMPTY; + } + URL url = new URL(urlStr); + String host = url.getHost(); + int port = url.getPort(); + if (port == INVALID_PORT) { + return host; + } + return host + COLON + port; + } catch (Exception e) { + FineLoggerFactory.getLogger().debug(e.getMessage(), e); + return urlStr; + } + } + + /** + * 透明的背景,需要从根节点重绘 + * + * @param component 组件 + */ + public static void repaintAll(JComponent component) { + + JRootPane rootPane = component.getRootPane(); + if (rootPane != null) { + rootPane.repaint(); + } + } } diff --git a/designer-base/src/main/java/com/fr/startup/ui/StartupPageWindow.java b/designer-base/src/main/java/com/fr/startup/ui/StartupPageWindow.java index fefe6e8ec..cb0442abd 100644 --- a/designer-base/src/main/java/com/fr/startup/ui/StartupPageWindow.java +++ b/designer-base/src/main/java/com/fr/startup/ui/StartupPageWindow.java @@ -11,9 +11,15 @@ import com.fr.design.layout.FRGUIPaneFactory; import com.fr.design.layout.VerticalFlowLayout; import com.fr.design.ui.util.UIUtil; import com.fr.design.utils.ColorUtils; -import com.fr.design.utils.gui.GUICoreUtils; +import com.fr.design.utils.ThemeUtils; +import com.fr.exit.DesignerExiter; +import com.fr.general.GeneralUtils; +import com.fr.general.IOUtils; import com.fr.log.FineLoggerFactory; +import com.fr.stable.ProductConstants; import com.fr.stable.collections.CollectionUtils; +import com.fr.start.common.DesignerStartupContext; +import com.fr.startup.metric.DesignerMetrics; import org.jetbrains.annotations.NotNull; import javax.swing.BorderFactory; @@ -39,6 +45,9 @@ import java.awt.LayoutManager; import java.awt.RenderingHints; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; +import java.awt.image.BufferedImage; import java.util.List; import java.util.Map; @@ -63,7 +72,7 @@ public class StartupPageWindow extends JFrame { private static final int TITLE_FONT_SIZE = 24; private static final int ITEM_VERTICAL_GAP = 5; - private static final Dimension SCREEN_SIZE = new Dimension(1600, 820); + private static final Dimension SCREEN_SIZE = java.awt.Toolkit.getDefaultToolkit().getScreenSize(); private StartupPageWorkspacePanel workspacePanel; @@ -91,6 +100,7 @@ public class StartupPageWindow extends JFrame { setLayout(new BorderLayout()); this.body = FRGUIPaneFactory.createBorderLayout_S_Pane(); + this.body.setBackground(new Color(0, 0, 0, 0)); // Header UILabel label = new UILabel(Toolkit.i18nText("Fine-Design_Startup_Page_Select_Workspace")); Font font = label.getFont(); @@ -100,6 +110,7 @@ public class StartupPageWindow extends JFrame { LayoutManager centerFlowLayout = FRGUIPaneFactory.createCenterFlowLayout(); headerPanel.setLayout(centerFlowLayout); headerPanel.add(label); + headerPanel.setBackground(new Color(0, 0, 0, 0)); this.body.add(headerPanel, BorderLayout.NORTH); // Workspace-description @@ -121,7 +132,14 @@ public class StartupPageWindow extends JFrame { this.recentOpenPanel = generateRecentOpenPanel(pageModel); this.body.add(recentOpenPanel, BorderLayout.SOUTH); - this.contentPane = new JPanel(); + this.contentPane = new JPanel() { + @Override + protected void paintComponent(Graphics g) { + super.paintComponent(g); + BufferedImage image = IOUtils.readImage("com/fr/design/startup/startup_page_background.jpg"); + g.drawImage(image, 0, 0, SCREEN_SIZE.width, SCREEN_SIZE.height, this); + } + }; this.contentPane.setLayout(getCenterLayout(body)); this.contentPane.add(this.body, BorderLayout.CENTER); this.contentPane.setPreferredSize(this.body.getPreferredSize()); @@ -135,12 +153,44 @@ public class StartupPageWindow extends JFrame { // Workspace-detail setSize(SCREEN_SIZE); + setDefaultTitle(); + addDefaultListeners(); repaint(); validate(); revalidate(); - GUICoreUtils.centerWindow(this); + setFullScreen(); + } + + private void setFullScreen() { + + Dimension screenSize = java.awt.Toolkit.getDefaultToolkit().getScreenSize(); + this.setLocation(0, 0); + this.setSize(screenSize.width, screenSize.height); + } + + private void addDefaultListeners() { + + addWindowListener(new WindowAdapter() { + @Override + public void windowClosing(WindowEvent e) { + DesignerExiter.getInstance().execute(); + } + }); + } + + private void setDefaultTitle() { + + StringBuilder sb = new StringBuilder(); + sb.append(ProductConstants.APP_NAME); + sb.append(" "); + sb.append(GeneralUtils.getVersion()); + sb.append(" "); + sb.append(ProductConstants.BRANCH); + sb.append(" "); + sb.append(Toolkit.i18nText("Fine-Design_Startup_Page_Title")); + setTitle(sb.toString()); } private void patchUIAction(StartupPageModel pageModel) { @@ -266,6 +316,7 @@ public class StartupPageWindow extends JFrame { recentOpenWrapperPanel.setBorder(new EmptyBorder(0, 0, 0, 20)); recentOpenWrapperPanel.add(recentOpenPanel, BorderLayout.CENTER); + ColorUtils.syncBackgroundIfAbsent(recentOpenWrapperPanel, new Color(0,0,0,0), ThemeUtils.BACK_COLOR); return recentOpenWrapperPanel; } @@ -294,17 +345,18 @@ public class StartupPageWindow extends JFrame { @Override public void mouseEntered(MouseEvent e) { recentFileLabel.setForeground(HOVER_COLOR); + StartupPageUtil.repaintAll(recentOpenGroupPanel); } @Override public void mouseExited(MouseEvent e) { recentFileLabel.setForeground(recentFileLabelForeground); + StartupPageUtil.repaintAll(recentOpenGroupPanel); } @Override public void mouseClicked(MouseEvent e) { - DesignerEnvManager.getEnvManager().setLastOpenFile(recentFile); - pageModel.getOpenLastTemplateRunnable().run(); + doOpenLastTemplateAction(recentFile, pageModel); } }); Dimension preferredSize = recentItemPanel.getPreferredSize(); @@ -317,7 +369,7 @@ public class StartupPageWindow extends JFrame { recentOpenGroupPanel.setPreferredSize(new Dimension(GROUP_WIDTH, (int) preferredSize.getHeight())); if (needScroll) { - int scrollHeight = (int) Math.round(itemHeight * RECENT_FILE_LIMIT + ITEM_VERTICAL_GAP * (RECENT_FILE_SCROLL)); + int scrollHeight = (int) Math.round(itemHeight * RECENT_FILE_LIMIT + ITEM_VERTICAL_GAP * (RECENT_FILE_LIMIT)); UIScrollPane scrollPane = new UIScrollPane(recentOpenGroupPanel, ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED, ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER); scrollPane.setBorder(new EmptyBorder(0, 0, 0, 0)); scrollPane.setPreferredSize(new Dimension(GROUP_WIDTH, scrollHeight)); @@ -327,8 +379,10 @@ public class StartupPageWindow extends JFrame { } private StartupPageWorkspacePanel generateWorkspacePanel(StartupPageModel pageModel) { - - return new StartupPageWorkspacePanel(pageModel); + + StartupPageWorkspacePanel startupPageWorkspacePanel = new StartupPageWorkspacePanel(pageModel); + ColorUtils.syncBackgroundIfAbsent(startupPageWorkspacePanel, new Color(0, 0, 0, 0), ThemeUtils.BACK_COLOR); + return startupPageWorkspacePanel; } protected LayoutManager getCenterLayout(JComponent centerBody) { @@ -336,4 +390,13 @@ public class StartupPageWindow extends JFrame { return FRGUIPaneFactory.createCenterLayout(centerBody); } + private void doOpenLastTemplateAction(String recentFile, StartupPageModel pageModel) { + + DesignerEnvManager.getEnvManager().setLastOpenFile(recentFile); + pageModel.getOpenLastTemplateRunnable().run(); + + DesignerMetrics designerMetrics = DesignerStartupContext.getInstance().getDesignerMetrics(); + designerMetrics.getStatistic().recordOpenLastTemplate(recentFile); + } + } diff --git a/designer-base/src/main/java/com/fr/startup/ui/StartupPageWorkspacePanel.java b/designer-base/src/main/java/com/fr/startup/ui/StartupPageWorkspacePanel.java index a22c32b03..ced2aa376 100644 --- a/designer-base/src/main/java/com/fr/startup/ui/StartupPageWorkspacePanel.java +++ b/designer-base/src/main/java/com/fr/startup/ui/StartupPageWorkspacePanel.java @@ -7,6 +7,8 @@ import com.fr.design.gui.ilable.UILabel; import com.fr.design.i18n.Toolkit; import com.fr.design.layout.FRGUIPaneFactory; import com.fr.design.utils.ColorUtils; +import com.fr.start.common.DesignerStartupContext; +import com.fr.startup.metric.DesignerMetrics; import com.fr.third.guava.collect.Lists; import org.jetbrains.annotations.NotNull; @@ -63,7 +65,10 @@ public class StartupPageWorkspacePanel extends JPanel { private static final Dimension PATH_DIMENSION = new Dimension(100, 20); private static final Dimension SELECT_WORKSPACE_DIMENSION = new Dimension(210, 72); private static final Dimension SELECT_CREATE_DIMENSION = new Dimension(60, 72); - public static final int COLUMN_LIMIT = 3; + + private static final int COLUMN_LIMIT = 3; + private static final int DOUBLE_CLICK_COUNT = 2; + public static final int PARTITION_LIMIT = 2; /* model */ @@ -86,6 +91,7 @@ public class StartupPageWorkspacePanel extends JPanel { public StartupPageWorkspacePanel(StartupPageModel pageModel) { this.setLayout(new BorderLayout(0, 0)); + this.setBorder(new EmptyBorder(15, 0, 20, 0)); this.pageModel = pageModel; @@ -95,12 +101,14 @@ public class StartupPageWorkspacePanel extends JPanel { this.contentPanel = generateLimitContentPanel(partitions); this.add(contentPanel, BorderLayout.NORTH); - this.tailPanel = generateTailPanel(); + if (partitions.size() > PARTITION_LIMIT) { + this.tailPanel = generateTailPanel(); + this.add(tailPanel, BorderLayout.SOUTH); + } this.createNewTemplateRunnable = pageModel.getCreateNewTemplateRunnable(); this.openEmptyTemplateRunnable = pageModel.getOpenEmptyTemplateRunnable(); - this.add(tailPanel, BorderLayout.SOUTH); this.repaint(); } @@ -122,8 +130,12 @@ public class StartupPageWorkspacePanel extends JPanel { private JComponent generateUnLimitContentPanel(List> partitions) { + JPanel workspaceDescWrapper = new JPanel(); + workspaceDescWrapper.setLayout(new BorderLayout(0, 0)); + workspaceDescWrapper.setBorder(new EmptyBorder(0, 0, 0, 0)); + JPanel workspaceDescPanel = new JPanel(); - workspaceDescPanel.setLayout(new GridLayout(partitions.size(), 1, 0, ITEM_VERTICAL_GAP)); + workspaceDescPanel.setLayout(new GridLayout(partitions.size(), 1, 0, 0)); for (List partition : partitions) { JPanel partitionPanel = generatePartitionPanel(partition); workspaceDescPanel.add(partitionPanel); @@ -134,14 +146,18 @@ public class StartupPageWorkspacePanel extends JPanel { UIScrollPane scrollPane = new UIScrollPane(workspaceDescPanel, ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED, ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER); scrollPane.setBorder(new EmptyBorder(10, 0, 0, 0)); scrollPane.setPreferredSize(new Dimension(CONTENT_WIDTH, SCROLL_HEIGHT)); - return scrollPane; + workspaceDescWrapper.add(scrollPane, BorderLayout.CENTER); + return workspaceDescWrapper; } - return workspaceDescPanel; + workspaceDescWrapper.add(workspaceDescPanel, BorderLayout.CENTER); + + ColorUtils.transparentBackground(workspaceDescWrapper); + return workspaceDescWrapper; } private JPanel generateLimitContentPanel(List> partitions) { - JPanel workspaceDescPanel = FRGUIPaneFactory.createVerticalFlowLayout_Pane(true, FlowLayout.LEFT, 0, ITEM_VERTICAL_GAP); + JPanel workspaceDescPanel = FRGUIPaneFactory.createVerticalFlowLayout_Pane(true, FlowLayout.LEFT, 0, 0); int limit = 2; for (int i = 0; i < partitions.size(); i++) { if (i >= limit) { @@ -152,22 +168,38 @@ public class StartupPageWorkspacePanel extends JPanel { JPanel partitionPanel = generatePartitionPanel(partition); workspaceDescPanel.add(partitionPanel); } + + ColorUtils.transparentBackground(workspaceDescPanel); + return workspaceDescPanel; } @NotNull private JPanel generateTailPanel() { + + AtomicReference hoverBackColorRef = new AtomicReference<>(); JPanel tailPanel = new JPanel(); { tailPanel.setLayout(new FlowLayout(FlowLayout.RIGHT)); tailPanel.setBorder(new EmptyBorder(0, 0, 0, 20)); - JPanel showAllPanel = new JPanel(); + JPanel showAllPanel = new JPanel() { + @Override + protected void paintComponent(Graphics g) { + super.paintComponent(g); + if (hoverBackColorRef.get() != null) { + g.setColor(hoverBackColorRef.get()); + Dimension preferredSize = getPreferredSize(); + g.fillRoundRect(0, 0, preferredSize.width, preferredSize.height, 5, 5); + } + } + }; showAllPanel.setLayout(new BorderLayout(5, 0)); showAllPanel.setBorder(new EmptyBorder(5, 5, 5, 5)); UILabel fontLabel = new UILabel(Toolkit.i18nText("Fine-Design_Startup_Page_Expand_All")); fontLabel.setForeground(HOVER_COLOR); + showAllPanel.setBackground(new Color(0, 0, 0, 0)); showAllPanel.add(fontLabel, BorderLayout.WEST); UILabel iconLabel = new UILabel(IconUtils.readIcon("/com/fr/design/startup/show_more.svg")); @@ -178,28 +210,21 @@ public class StartupPageWorkspacePanel extends JPanel { showAllPanel.addMouseListener(new MouseAdapter() { @Override public void mouseEntered(MouseEvent e) { - Color hoverColor = new Color(217, 235, 254); - showAllPanel.setBackground(hoverColor); + Color hoverBackColor = new Color(217, 235, 254); + hoverBackColorRef.set(hoverBackColor); + repaintAll(); } @Override public void mouseExited(MouseEvent e) { + hoverBackColorRef.set(null); ColorUtils.syncBackground(showAllPanel, showAllBackground); + repaintAll(); } @Override public void mousePressed(MouseEvent e) { - if (showMore) { - fontLabel.setText(Toolkit.i18nText("Fine-Design_Startup_Page_Collapse_Workspace")); - iconLabel.setIcon(IconUtils.readIcon("/com/fr/design/startup/show_less.svg")); - showMoreContent(); - showMore = !showMore; - } else { - fontLabel.setText(Toolkit.i18nText("Fine-Design_Startup_Page_Expand_All")); - iconLabel.setIcon(IconUtils.readIcon("/com/fr/design/startup/show_more.svg")); - showLessContent(); - showMore = !showMore; - } + doShowAllAction(fontLabel, iconLabel); } }); tailPanel.add(showAllPanel); @@ -212,7 +237,11 @@ public class StartupPageWorkspacePanel extends JPanel { @NotNull private JPanel generatePartitionPanel(List partition) { - JPanel partitionPanel = FRGUIPaneFactory.createBoxFlowInnerContainer_S_Pane(0, 20, 0);; + JPanel partitionPanelWrapper = new JPanel(); + partitionPanelWrapper.setBorder(new EmptyBorder(10,0,10,0)); + partitionPanelWrapper.setLayout(new BorderLayout()); + + JPanel partitionPanel = FRGUIPaneFactory.createBoxFlowInnerContainer_S_Pane(0, 20, 0); partitionPanel.setName("partitionPanel"); for (StartupWorkspaceBean workspaceInfo : partition) { @@ -221,14 +250,16 @@ public class StartupPageWorkspacePanel extends JPanel { layoutSelectWorkspacePanel(workspaceInfo, workspaceItemDesc); - layoutSelectAndCreatePanel(workspaceItemDesc); + layoutSelectAndCreatePanel(workspaceInfo, workspaceItemDesc); partitionPanel.add(workspaceItemDesc); Dimension preferredSize = partitionPanel.getPreferredSize(); partitionPanel.setPreferredSize(new Dimension(CONTENT_WIDTH, (int) preferredSize.getHeight())); } - return partitionPanel; + + partitionPanelWrapper.add(partitionPanel, BorderLayout.CENTER); + return partitionPanelWrapper; } private void layoutSelectWorkspacePanel(StartupWorkspaceBean workspaceInfo, JPanel workspaceItemDesc) { @@ -295,8 +326,6 @@ public class StartupPageWorkspacePanel extends JPanel { int fixRoundWidth = getWidth() - rectOffset; int fixRoundHeight = getHeight() - BORDER_THIN; g2d.drawRoundRect(strokeOffset, strokeOffset, fixRoundWidth, fixRoundHeight, ARC_DIAMETER, ARC_DIAMETER); - // 画一个直角 - g2d.drawRoundRect(getWidth() - roundOffset, strokeOffset, roundOffset - strokeOffset, getHeight() - BORDER_THIN, 0, 0); g2d.setColor(backColor); @@ -306,8 +335,13 @@ public class StartupPageWorkspacePanel extends JPanel { // 偏左一点的 fixedX int fixedX = getWidth() - roundOffset - BORDER_THIN; // 圆角和直角相交的区域 - int coverWidth = 10; + int coverWidth = 15; g2d.fillRect(fixedX, BORDER_THIN, coverWidth, coverHeight); + + g2d.setColor(borderColor); + g2d.drawLine(getWidth() / 2, strokeOffset, getWidth(), strokeOffset); + g2d.drawLine(getWidth() / 2, getHeight() - strokeOffset, getWidth(), getHeight() - strokeOffset); + g2d.drawLine(getWidth() - strokeOffset, strokeOffset, getWidth() - strokeOffset, getHeight() - strokeOffset); } } }; @@ -335,6 +369,7 @@ public class StartupPageWorkspacePanel extends JPanel { Font font = nameLabel.getFont(); Font newSizeFont = font.deriveFont(font.getStyle(), NAME_LABEL_SIZE); nameLabel.setFont(newSizeFont); + nameLabel.setPreferredSize(PATH_DIMENSION); Color nameForeground = nameLabel.getForeground(); simpleDescPanel.add(nameLabel,BorderLayout.NORTH); @@ -357,7 +392,7 @@ public class StartupPageWorkspacePanel extends JPanel { borderColorRef.set(hoverColor); nameLabel.setForeground(hoverColor); pathLabel.setForeground(hoverColor ); - selectWorkspacePanel.getParent().repaint(); + repaintAll(); } @Override @@ -365,21 +400,18 @@ public class StartupPageWorkspacePanel extends JPanel { borderColorRef.set(Color.WHITE); nameLabel.setForeground(nameForeground); pathLabel.setForeground(pathColor); - selectWorkspacePanel.getParent().repaint(); + repaintAll(); } @Override public void mousePressed(MouseEvent e) { int clickCount = e.getClickCount(); - if (clickCount == BORDER_THIN) { - pageModel.setSelectWorkspaceInfo(workspaceInfo); - openEmptyTemplateRunnable.run(); + if (clickCount == DOUBLE_CLICK_COUNT) { + doOpenEmptyTemplate(workspaceInfo); return; } - // selectWorkspaceRunnable - pageModel.setSelectWorkspaceInfo(workspaceInfo); - selectWorkspaceRunnable.run(); + doSwitchWorkspace(workspaceInfo); } }; @@ -406,7 +438,7 @@ public class StartupPageWorkspacePanel extends JPanel { @Override public void mousePressed(MouseEvent e) { - openEmptyTemplateRunnable.run(); + doOpenEmptyTemplate(workspaceInfo); } }); descPanel.add(arrowLabel, BorderLayout.EAST); @@ -420,7 +452,7 @@ public class StartupPageWorkspacePanel extends JPanel { workspaceItemDesc.add(selectWorkspacePanel, BorderLayout.WEST); } - private void layoutSelectAndCreatePanel(JPanel workspaceItemDesc) { + private void layoutSelectAndCreatePanel(StartupWorkspaceBean workspaceInfo, JPanel workspaceItemDesc) { // 选择并新建 AtomicReference borderColorRef = new AtomicReference<>(null); @@ -450,12 +482,18 @@ public class StartupPageWorkspacePanel extends JPanel { if (borderColor != null) { g2d.setColor(borderColor); g2d.setStroke(new BasicStroke(BORDER_THIN)); + + int borderOffset = BORDER_THIN * 2; // 画画的笔触需要调整一下 - g2d.drawRoundRect(strokeOffset, strokeOffset, getWidth() - rectOffset, getHeight() - BORDER_THIN, 0, 0); - g2d.drawRoundRect(getWidth() - fixedRoundOffset, strokeOffset, roundOffset - strokeOffset, getHeight() - BORDER_THIN, ARC_DIAMETER, ARC_DIAMETER); + g2d.drawRoundRect(strokeOffset, strokeOffset, getWidth() - borderOffset, getHeight() - BORDER_THIN, ARC_DIAMETER, ARC_DIAMETER); g2d.setColor(backColor); - int fillWidth = 11; - g2d.fillRect(getWidth() - fixedRoundOffset - BORDER_THIN, BORDER_THIN, fillWidth, getHeight() - BORDER_THIN * 2); + int fillWidth = 15; + g2d.fillRect(0, 0, fillWidth, getHeight()); + + g2d.setColor(borderColor); + g2d.drawLine(strokeOffset, strokeOffset, fillWidth, strokeOffset); + g2d.drawLine(strokeOffset, getHeight() - strokeOffset, fillWidth, getHeight() - strokeOffset); + g2d.drawLine(strokeOffset, strokeOffset, strokeOffset, getHeight() - strokeOffset); } } @@ -472,18 +510,19 @@ public class StartupPageWorkspacePanel extends JPanel { @Override public void mouseEntered(MouseEvent e) { borderColorRef.set(HOVER_COLOR); - selectAndCreatePanel.getParent().repaint(); label.setIcon(IconUtils.readIcon("/com/fr/design/standard/system/add_hover.svg")); + repaintAll(); } @Override public void mouseExited(MouseEvent e) { borderColorRef.set(null); - selectAndCreatePanel.getParent().repaint(); label.setIcon(IconUtils.readIcon("/com/fr/design/standard/system/add.svg")); + repaintAll(); } @Override public void mousePressed(MouseEvent e) { + pageModel.setSelectWorkspaceInfo(workspaceInfo); createNewTemplateRunnable.run(); } }); @@ -498,4 +537,47 @@ public class StartupPageWorkspacePanel extends JPanel { this.selectWorkspaceRunnable = selectWorkspaceRunnable; } + private void doOpenEmptyTemplate(StartupWorkspaceBean workspaceInfo) { + + pageModel.setSelectWorkspaceInfo(workspaceInfo); + openEmptyTemplateRunnable.run(); + + DesignerMetrics designerMetrics = DesignerStartupContext.getInstance().getDesignerMetrics(); + designerMetrics.getStatistic().recordOpenEmptyTemplate(); + } + + private void doSwitchWorkspace(StartupWorkspaceBean workspaceInfo) { + + StartupWorkspaceBean lastWorkspaceInfo = pageModel.getSelectWorkspaceInfo(); + // selectWorkspaceRunnable + pageModel.setSelectWorkspaceInfo(workspaceInfo); + selectWorkspaceRunnable.run(); + + DesignerMetrics designerMetrics = DesignerStartupContext.getInstance().getDesignerMetrics(); + designerMetrics.getStatistic().recordSwitchWorkspace(lastWorkspaceInfo, workspaceInfo); + } + + private void doShowAllAction(UILabel fontLabel, UILabel iconLabel) { + + if (showMore) { + fontLabel.setText(Toolkit.i18nText("Fine-Design_Startup_Page_Collapse_Workspace")); + iconLabel.setIcon(IconUtils.readIcon("/com/fr/design/startup/show_less.svg")); + showMoreContent(); + showMore = !showMore; + } else { + fontLabel.setText(Toolkit.i18nText("Fine-Design_Startup_Page_Expand_All")); + iconLabel.setIcon(IconUtils.readIcon("/com/fr/design/startup/show_more.svg")); + showLessContent(); + showMore = !showMore; + } + DesignerMetrics designerMetrics = DesignerStartupContext.getInstance().getDesignerMetrics(); + designerMetrics.getStatistic().recordShowAllAction(); + + repaintAll(); + } + + private void repaintAll() { + + this.getRootPane().repaint(); + } } 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 000000000..935503e65 Binary files /dev/null and b/designer-base/src/main/resources/com/fr/design/images/FileDealerPaneIcon/collapse-all.png differ 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 000000000..0773525e3 Binary files /dev/null and b/designer-base/src/main/resources/com/fr/design/images/FileDealerPaneIcon/locate.png differ 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 000000000..9d924c78d Binary files /dev/null and b/designer-base/src/main/resources/com/fr/design/images/m_edit/move.png differ diff --git a/designer-base/src/main/resources/com/fr/design/startup/startup_page_background.jpg b/designer-base/src/main/resources/com/fr/design/startup/startup_page_background.jpg new file mode 100755 index 000000000..ad0c5de36 Binary files /dev/null and b/designer-base/src/main/resources/com/fr/design/startup/startup_page_background.jpg differ diff --git a/designer-base/src/test/java/com/fr/startup/ui/StartupPageUtilTest.java b/designer-base/src/test/java/com/fr/startup/ui/StartupPageUtilTest.java new file mode 100644 index 000000000..fe69541e3 --- /dev/null +++ b/designer-base/src/test/java/com/fr/startup/ui/StartupPageUtilTest.java @@ -0,0 +1,21 @@ +package com.fr.startup.ui; + +import org.junit.Assert; +import org.junit.Test; + +import static org.junit.Assert.*; + +public class StartupPageUtilTest { + + @Test + public void testGetRemoteAddress() throws Exception { + String remoteAddress = StartupPageUtil.getRemoteAddress("https://localhost:9090/webroot"); + Assert.assertEquals("localhost:9090", remoteAddress); + + String remoteAddress1 = StartupPageUtil.getRemoteAddress("https://localhost/webroot"); + Assert.assertEquals("localhost", remoteAddress1); + + String remoteAddress2 = StartupPageUtil.getRemoteAddress(null); + Assert.assertEquals("", remoteAddress2); + } +} \ No newline at end of file diff --git a/designer-base/src/test/java/com/fr/startup/ui/StartupPageWindowTest.java b/designer-base/src/test/java/com/fr/startup/ui/StartupPageWindowTest.java index c1d33a66a..804bb7d3f 100644 --- a/designer-base/src/test/java/com/fr/startup/ui/StartupPageWindowTest.java +++ b/designer-base/src/test/java/com/fr/startup/ui/StartupPageWindowTest.java @@ -16,13 +16,13 @@ public class StartupPageWindowTest { @Override public void run() { HashMap> recentOpenFileMap = new HashMap<>(); - recentOpenFileMap.put("111", Lists.newArrayList("111.cpt", "222//3333.cpt","333.cpt", "444.cpt", "555.cpt", "666.cpt")); + recentOpenFileMap.put("111", Lists.newArrayList("111.cpt", "222//3333.cpt","333.cpt", "444.cpt", "555.cpt", "666.cpt", "777.cpt")); StartupPageModel model = new StartupPageModel(Stream.of( StartupWorkspaceBean.create("111", "222333344455556663333444555566633334445555666"), StartupWorkspaceBean.create("113", "222"), StartupWorkspaceBean.create("114", "222"), StartupWorkspaceBean.create("115", "222"), StartupWorkspaceBean.create("116", "222"), - StartupWorkspaceBean.create("117", "222"), StartupWorkspaceBean.create("118", "222"), StartupWorkspaceBean.create("119", "222"), - StartupWorkspaceBean.create("121", "222"), StartupWorkspaceBean.create("122", "222"), StartupWorkspaceBean.create("123", "222"), - StartupWorkspaceBean.create("124", "222"), StartupWorkspaceBean.create("125", "222"), StartupWorkspaceBean.create("126", "222") + //StartupWorkspaceBean.create("117", "222"), StartupWorkspaceBean.create("118", "222"), StartupWorkspaceBean.create("119", "222"), + //StartupWorkspaceBean.create("121", "222"), StartupWorkspaceBean.create("122", "222"), StartupWorkspaceBean.create("123", "222"), + StartupWorkspaceBean.create("1245678888888", "222"), StartupWorkspaceBean.create("125", "222"), StartupWorkspaceBean.create("126", "222") ).collect(Collectors.toList()), recentOpenFileMap); StartupPageWindow window = new StartupPageWindow(model); window.setVisible(true); diff --git a/designer-form/src/main/java/com/fr/design/designer/creator/cardlayout/XWCardTagLayout.java b/designer-form/src/main/java/com/fr/design/designer/creator/cardlayout/XWCardTagLayout.java index dbffab21a..0353d53dd 100644 --- a/designer-form/src/main/java/com/fr/design/designer/creator/cardlayout/XWCardTagLayout.java +++ b/designer-form/src/main/java/com/fr/design/designer/creator/cardlayout/XWCardTagLayout.java @@ -35,6 +35,7 @@ import com.fr.general.act.BorderPacker; import com.fr.general.ComparatorUtils; import com.fr.general.FRFont; import com.fr.general.cardtag.DefaultTemplateStyle; +import com.fr.stable.StringUtils; import javax.swing.border.Border; import java.awt.*; @@ -132,14 +133,16 @@ public class XWCardTagLayout extends XWHorizontalBoxLayout { } int index = this.cardLayout.toData().getWidgetCount(); - //新加一个card - String widgetName = tagName + getTabNameIndex(); + //新加一个card,命名规则是tabxy,x为tablayout中tab的index,y为模板中tablayout 的index + String widgetName = tagName + getTabNameIndex() + getCardLayoutSuffix(cardLayout.toData().getWidgetName(), cardLayout.createDefaultName()); WTabFitLayout fitLayout = new WTabFitLayout(widgetName, tabFitIndex, currentCard); fitLayout.setTabNameIndex(getTabNameIndex()); XWTabFitLayout tabFitLayout = new XWTabFitLayout(fitLayout, new Dimension()); FormDesigner formDesigner = WidgetPropertyPane.getInstance().getEditingFormDesigner(); - ModelUtil.renameWidgetName(formDesigner.getTarget(), tabFitLayout); + if (formDesigner.getTarget().isNameExist(widgetName)) { + ModelUtil.renameWidgetName(formDesigner.getTarget(), tabFitLayout); + } WCardTagLayout layout = (WCardTagLayout) this.toData(); if(!ComparatorUtils.equals(layout.getTemplateStyle().getStyle(), DefaultTemplateStyle.DEFAULT_TEMPLATE_STYLE)){ @@ -154,6 +157,13 @@ public class XWCardTagLayout extends XWHorizontalBoxLayout { cardLayout.showCard(); } + private String getCardLayoutSuffix(String cardLayoutName, String defaultName){ + if (StringUtils.isEmpty(cardLayoutName) || StringUtils.isEmpty(defaultName) || !cardLayoutName.contains(defaultName)){ + return StringUtils.EMPTY; + } + return cardLayoutName.substring(defaultName.length()); + } + @Override protected String getIconName() { diff --git a/designer-realize/src/main/java/com/fr/design/actions/server/WidgetManagerAction.java b/designer-realize/src/main/java/com/fr/design/actions/server/WidgetManagerAction.java index ff8352e28..d04dc7697 100644 --- a/designer-realize/src/main/java/com/fr/design/actions/server/WidgetManagerAction.java +++ b/designer-realize/src/main/java/com/fr/design/actions/server/WidgetManagerAction.java @@ -1,16 +1,15 @@ package com.fr.design.actions.server; -import com.fr.base.svg.IconUtils; import com.fr.design.DesignModelAdapter; import com.fr.design.actions.UpdateAction; import com.fr.design.dialog.BasicDialog; import com.fr.design.dialog.DialogActionAdapter; import com.fr.design.mainframe.DesignerContext; import com.fr.design.mainframe.DesignerFrame; +import com.fr.design.mainframe.JTemplate; import com.fr.design.menu.MenuKeySet; import com.fr.design.webattr.WidgetManagerPane; import com.fr.form.ui.WidgetInfoConfig; - import com.fr.transaction.CallBackAdaptor; import com.fr.transaction.Configurations; import com.fr.transaction.WorkerFacade; @@ -61,7 +60,10 @@ public class WidgetManagerAction extends UpdateAction { if (model != null) { model.widgetConfigChanged(); } - designerFrame.getSelectedJTemplate().refreshToolArea(); + JTemplate jt = designerFrame.getSelectedJTemplate(); + if (JTemplate.isValid(jt)) { + jt.refreshToolArea(); + } } })); } diff --git a/designer-realize/src/main/java/com/fr/design/deeplink/FileOpen4MacDeepLink.java b/designer-realize/src/main/java/com/fr/design/deeplink/FileOpen4MacDeepLink.java index 5709aabd6..ce69e0823 100644 --- a/designer-realize/src/main/java/com/fr/design/deeplink/FileOpen4MacDeepLink.java +++ b/designer-realize/src/main/java/com/fr/design/deeplink/FileOpen4MacDeepLink.java @@ -4,6 +4,8 @@ import com.fr.design.mainframe.DesignerContext; import com.fr.file.FileFILE; import com.fr.stable.StringUtils; import com.fr.stable.os.OperatingSystem; +import com.fr.start.common.DesignerStartupContext; +import com.fr.start.common.DesignerStartupUtil; import java.io.File; import java.util.Map; @@ -13,17 +15,36 @@ import java.util.Map; * @version 1.0 * Created by Starryi on 2022/1/13 */ -public class FileOpen4MacDeepLink extends DeepLink { +public class FileOpen4MacDeepLink extends DeepLink implements DeepLinkPrepare{ @Override public boolean accept(String url, String host, String path, Map params) { - return OperatingSystem.isMacos() && StringUtils.isEmpty(host) && StringUtils.isEmpty(path) && params.isEmpty(); + + return support(url, host, path, params); } @Override public void run(String url, String host, String path, Map params) { File file = new File(url); if (file.exists()) { + if (DesignerStartupUtil.openTemplateIfOnWaiting(file)) { + return; + } DesignerContext.getDesignerFrame().openTemplate(new FileFILE(file)); } } + + @Override + public boolean prepare(String url, String host, String path, Map params) { + + if (support(url, host, path, params)) { + DesignerStartupContext.getInstance().setEnabled(false); + return true; + } + return false; + } + + public static boolean support(String url, String host, String path, Map params) { + + return OperatingSystem.isMacos() && StringUtils.isEmpty(host) && StringUtils.isEmpty(path) && params.isEmpty(); + } } diff --git a/designer-realize/src/main/java/com/fr/design/mainframe/ElementCasePaneDelegate.java b/designer-realize/src/main/java/com/fr/design/mainframe/ElementCasePaneDelegate.java index 5cc095cfa..66f97093e 100644 --- a/designer-realize/src/main/java/com/fr/design/mainframe/ElementCasePaneDelegate.java +++ b/designer-realize/src/main/java/com/fr/design/mainframe/ElementCasePaneDelegate.java @@ -86,7 +86,7 @@ public class ElementCasePaneDelegate extends ElementCasePane { QuickEditorRegion.getInstance().populate(getCurrentEditor()); JTemplate editingTemplate = HistoryTemplateListPane.getInstance().getCurrentEditingTemplate(); // 模板初始化完成后,才能初始化超级链接面板 - if (editingTemplate != null && !editingTemplate.isUpMode()) { + if (JTemplate.isValid(editingTemplate) && !editingTemplate.isUpMode()) { Selection editingSelection = getSelection(); // 获取超级链接面板并刷新显示 HyperlinkGroupPane hyperlinkGroupPane = editingTemplate.getHyperLinkPane(HyperlinkGroupPaneActionImpl.getInstance()); diff --git a/designer-realize/src/main/java/com/fr/design/mainframe/alphafine/AlphaFineConstants.java b/designer-realize/src/main/java/com/fr/design/mainframe/alphafine/AlphaFineConstants.java index 8de2a0d63..935d2d0e3 100644 --- a/designer-realize/src/main/java/com/fr/design/mainframe/alphafine/AlphaFineConstants.java +++ b/designer-realize/src/main/java/com/fr/design/mainframe/alphafine/AlphaFineConstants.java @@ -5,8 +5,9 @@ import com.fr.base.svg.IconUtils; import com.fr.design.i18n.Toolkit; import com.fr.design.utils.DesignUtils; import com.fr.general.CloudCenter; - import com.fr.general.IOUtils; + +import javax.swing.Icon; import java.awt.Color; import java.awt.Dimension; import java.awt.Font; @@ -14,7 +15,6 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.LinkedHashSet; import java.util.Set; -import javax.swing.Icon; /** @@ -81,6 +81,8 @@ public class AlphaFineConstants { public static final Color WHITE = new Color(0xf9f9f9); + public static final Color LABEL_SELECTED = new Color(0x419bf9); + public static final Color GRAY = new Color(0xd2d2d2); public static final Color LIGHT_GRAY = new Color(0xcccccc); @@ -154,9 +156,21 @@ public class AlphaFineConstants { public static final String ALPHA_PREVIEW = CloudCenter.getInstance().acquireUrlByKind("af.preview"); - public static final String ALPHA_CID = CloudCenter.getInstance().acquireUrlByKind("af.cid", "https://cid.fanruan.com/api/nav/alphafine"); + public static final String ALPHA_CID = CloudCenter.getInstance().acquireUrlByKind("af.cid.new"); + + public static final String ALPHA_CID_USER_GROUP_INFO = CloudCenter.getInstance().acquireUrlByKind("af.cid.user.group.info"); + + public static final String SEARCH_BY_ID = "?id="; + + private static final String QUICK_START_URL = CloudCenter.getInstance().acquireUrlByKind("af.help.quick.start"); + private static final String REPORT_LEARNING_PATH = CloudCenter.getInstance().acquireUrlByKind("af.help.report.learning.path"); + private static final String PARAMETER_LEARNING_PATH = CloudCenter.getInstance().acquireUrlByKind("af.help.param.learning.path"); + private static final String FILL_LEARNING_PATH = CloudCenter.getInstance().acquireUrlByKind("af.help.fill.learning.path"); + private static final String API_SUMMARY = CloudCenter.getInstance().acquireUrlByKind("af.help.api.summary"); + private static final String MONTHLY_DOCUMENT = CloudCenter.getInstance().acquireUrlByKind("af.help.monthly.document"); + - private static final String DEFAULT_RECOMMEND = "[ { \"name\":\"快速入门指南\", \"link\":\"https://help.fanruan.com/finereport/doc-view-1335.html?source=3\" }, { \"name\":\"报表应用学习路径\", \"link\":\"https://help.fanruan.com/finereport/doc-view-1336.html?source=3\" }, { \"name\":\"参数应用学习路径\", \"link\":\"https://help.fanruan.com/finereport/doc-view-4219.html?source=3\" }, { \"name\":\"填报学习路径\", \"link\":\"https://help.fanruan.com/finereport/doc-view-4103.html?source=3\" }, { \"name\":\"API接口汇总\", \"link\":\"https://help.fanruan.com/finereport/doc-view-4327.html?source=3\" }, { \"name\":\"文档月刊\", \"link\":\"https://help.fanruan.com/finereport/doc-view-4613.html?source=3\" } ]"; + private static final String DEFAULT_RECOMMEND = "[ { \"name\":\"快速入门指南\", \"link\":\"" + QUICK_START_URL + "\" }, { \"name\":\"报表应用学习路径\", \"link\":\"" + REPORT_LEARNING_PATH + "\" }, { \"name\":\"参数应用学习路径\", \"link\":\"" + PARAMETER_LEARNING_PATH + "\" }, { \"name\":\"填报学习路径\", \"link\":\"" + FILL_LEARNING_PATH + "\" }, { \"name\":\"API接口汇总\", \"link\":\"" + API_SUMMARY + "\" }, { \"name\":\"文档月刊\", \"link\":\"" + MONTHLY_DOCUMENT + "\" } ]"; public static final String ALPHA_HELP_RECOMMEND = CloudCenter.getInstance().acquireUrlByKind("af.recommend", DEFAULT_RECOMMEND); diff --git a/designer-realize/src/main/java/com/fr/design/mainframe/alphafine/CellType.java b/designer-realize/src/main/java/com/fr/design/mainframe/alphafine/CellType.java index c558ee584..fc01f668d 100644 --- a/designer-realize/src/main/java/com/fr/design/mainframe/alphafine/CellType.java +++ b/designer-realize/src/main/java/com/fr/design/mainframe/alphafine/CellType.java @@ -17,7 +17,8 @@ public enum CellType { RECOMMEND_ROBOT(8), BOTTOM(9), ROBOT(10), - PRODUCT_NEWS(11, "productNews", "productNewsResult", true); + PRODUCT_NEWS(11, "productNews", "productNewsResult", true, false), + TEMPLATE_SHOP(12, "templateShop", "templateShop", false, true); private int typeValue; @@ -35,11 +36,19 @@ public enum CellType { private boolean needNetWork = true; - CellType(int type, String flagStr4None, String flagStr4Result, boolean needNetWork) { + private boolean canLocalSearch = false; + + + CellType(int type, String flagStr4None, String flagStr4Result, boolean needNetWork, boolean canLocalSearch) { this.typeValue = type; this.flagStr4None = flagStr4None; this.flagStr4Result = flagStr4Result; this.needNetWork = needNetWork; + this.canLocalSearch = canLocalSearch; + } + + CellType(int type, String flagStr4None, String flagStr4Result, boolean needNetWork) { + this(type, flagStr4None, flagStr4Result, needNetWork, false); } CellType(int type) { @@ -75,5 +84,8 @@ public enum CellType { public boolean isNeedNetWork() { return needNetWork; } -} + public boolean isCanLocalSearch() { + return canLocalSearch; + } +} \ No newline at end of file diff --git a/designer-realize/src/main/java/com/fr/design/mainframe/alphafine/action/StartUseAction.java b/designer-realize/src/main/java/com/fr/design/mainframe/alphafine/action/StartUseAction.java new file mode 100644 index 000000000..2315fbb0e --- /dev/null +++ b/designer-realize/src/main/java/com/fr/design/mainframe/alphafine/action/StartUseAction.java @@ -0,0 +1,86 @@ +package com.fr.design.mainframe.alphafine.action; + +import com.fr.common.util.Strings; +import com.fr.design.mainframe.DesignerContext; +import com.fr.design.mainframe.alphafine.AlphaFineHelper; +import com.fr.design.mainframe.alphafine.download.FineMarketConstants; +import com.fr.design.mainframe.alphafine.download.FineMarketDownloadManager; +import com.fr.design.mainframe.alphafine.model.TemplateResourceDetail; +import com.fr.file.FileFILE; +import com.fr.log.FineLoggerFactory; + +import javax.swing.SwingWorker; +import java.awt.Desktop; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.io.File; +import java.io.IOException; + + +/** + * alphaFine - 模板资源 - 二级界面 - 开始使用按钮的绑定事件 + * + * 点击后跳转至帆软市场下载对应模板资源 + * + * TODO:可以参考mini组件商城的下载@ComponentsPackageInstallation#install + * */ +public class StartUseAction implements ActionListener { + + TemplateResourceDetail resourceDetail; + + public StartUseAction(TemplateResourceDetail detail) { + this.resourceDetail = detail; + } + + @Override + public void actionPerformed(ActionEvent e) { + new SwingWorker() { + + @Override + protected String doInBackground() throws Exception { + return FineMarketDownloadManager.getInstance().installResource(resourceDetail.getRoot(), AlphaFineHelper.getAlphaFineDialog()); + } + + @Override + protected void done() { + try { + open(get()); + } catch (Exception e) { + FineLoggerFactory.getLogger().error(e, e.getMessage()); + } + super.done(); + } + }.execute(); + } + + void open(String fileName) throws IOException { + if (Strings.isEmpty(fileName)) { + return; + } + File fileNeedOpen = new File(fileName); + if (fileName.endsWith(FineMarketConstants.ZIP)) { + File[] files = fileNeedOpen.listFiles(); + fileNeedOpen = getFirstCptOrFrm(files); + } else if (fileName.endsWith(FineMarketConstants.RAR)) { + // rar直接打开系统文件夹 + File parentDir = new File(fileName).getParentFile(); + Desktop.getDesktop().open(parentDir); + return; + } + openInDesigner(fileNeedOpen); + } + + void openInDesigner(File file) { + DesignerContext.getDesignerFrame().openTemplate(new FileFILE(file)); + } + + + private File getFirstCptOrFrm(File[] files) { + for (File f : files) { + if (f.getName().endsWith(FineMarketConstants.CPT) || f.getName().endsWith(FineMarketConstants.FRM)) { + return f; + } + } + return null; + } +} diff --git a/designer-realize/src/main/java/com/fr/design/mainframe/alphafine/component/AlphaFineFrame.java b/designer-realize/src/main/java/com/fr/design/mainframe/alphafine/component/AlphaFineFrame.java index b6819f56c..bfd40aea3 100644 --- a/designer-realize/src/main/java/com/fr/design/mainframe/alphafine/component/AlphaFineFrame.java +++ b/designer-realize/src/main/java/com/fr/design/mainframe/alphafine/component/AlphaFineFrame.java @@ -22,20 +22,34 @@ import com.fr.design.mainframe.alphafine.preview.NoResultPane; import com.fr.design.mainframe.alphafine.preview.NoResultWithLinkPane; import com.fr.design.mainframe.alphafine.preview.SearchLoadingPane; import com.fr.design.mainframe.alphafine.preview.SimpleRightSearchResultPane; +import com.fr.design.mainframe.alphafine.preview.TemplateShopPane; import com.fr.design.mainframe.alphafine.question.QuestionWindow; import com.fr.design.mainframe.alphafine.search.ProductNewsSearchWorkerManager; import com.fr.design.mainframe.alphafine.search.SearchTextBean; import com.fr.design.mainframe.alphafine.search.SearchWorkerManager; +import com.fr.design.mainframe.alphafine.search.TemplateResourceSearchWorkerManager; import com.fr.design.mainframe.alphafine.search.manager.impl.ActionSearchManager; import com.fr.design.mainframe.alphafine.search.manager.impl.DocumentSearchManager; import com.fr.design.mainframe.alphafine.search.manager.impl.FileSearchManager; import com.fr.design.mainframe.alphafine.search.manager.impl.PluginSearchManager; import com.fr.design.mainframe.alphafine.search.manager.impl.ProductNewsSearchManager; import com.fr.design.mainframe.alphafine.search.manager.impl.SegmentationManager; +import com.fr.design.mainframe.alphafine.search.manager.impl.TemplateResourceSearchManager; import com.fr.design.utils.DesignUtils; import com.fr.design.utils.gui.GUICoreUtils; import com.fr.general.ComparatorUtils; import com.fr.stable.StringUtils; + +import javax.swing.BorderFactory; +import javax.swing.Icon; +import javax.swing.JButton; +import javax.swing.JFrame; +import javax.swing.JPanel; +import javax.swing.JPopupMenu; +import javax.swing.SwingConstants; +import javax.swing.Timer; +import javax.swing.event.PopupMenuEvent; +import javax.swing.event.PopupMenuListener; import java.awt.BorderLayout; import java.awt.CardLayout; import java.awt.Color; @@ -62,16 +76,6 @@ import java.util.List; import java.util.Set; import java.util.regex.Matcher; import java.util.regex.Pattern; -import javax.swing.BorderFactory; -import javax.swing.Icon; -import javax.swing.JButton; -import javax.swing.JFrame; -import javax.swing.JPanel; -import javax.swing.JPopupMenu; -import javax.swing.SwingConstants; -import javax.swing.Timer; -import javax.swing.event.PopupMenuEvent; -import javax.swing.event.PopupMenuListener; /** * @author hades @@ -98,7 +102,7 @@ public class AlphaFineFrame extends JFrame { private static final String PLACE_HOLDER = com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Report_AlphaFine"); - private static final String SETTING = com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Report_Set"); + private static final String FUNCTION = com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Report_Function"); private static final String NO_RESULT = com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Report_No_Result"); @@ -110,7 +114,7 @@ public class AlphaFineFrame extends JFrame { private static final String GO_FORUM = com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Report_AlphaFine_Go_Forum"); - private static final String TEMPLATES = com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Report_Templates"); + private static final String MY_TEMPLATES = com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Report_My_Templates"); public static final String PRODUCT_NEWS = com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Report_AlphaFine_Product_News"); @@ -122,7 +126,7 @@ public class AlphaFineFrame extends JFrame { private static final String NO_SEARCH_RESULT = com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Report_AlphaFine_NO_Result"); - private static final String PRODUCT_DYNAMICS = com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Report_AlphaFine_Product_Dynamics"); + private static final String TEMPLATE_SHOP = com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Report_AlphaFine_Template_Shop"); private static final Image SEARCH_IMAGE = SVGLoader.load("/com/fr/design/mainframe/alphafine/images/search.svg"); @@ -152,7 +156,19 @@ public class AlphaFineFrame extends JFrame { private JPanel tabPane; - private CellType selectedType; + private JPanel labelPane; + + private JPanel labelContentPane; + + private JPanel labelEastPane; + + private JPanel labelWestPane; + + private UILabel tabLabel; + + private UILabel readLabel; + + private SelectedLabel selectedTab; private String beforeSearchStr = StringUtils.EMPTY; @@ -168,15 +184,38 @@ public class AlphaFineFrame extends JFrame { private ProductNewsSearchWorkerManager productNewsSearchWorkerManager; + private TemplateResourceSearchWorkerManager templateResourceSearchWorkerManager; + public AlphaFineFrame() { this.setTitle(AlphaFineConstants.TITLE); + //去掉边框 setUndecorated(true); - setSize(AlphaFineConstants.FIELD_SIZE); + setSize(AlphaFineConstants.FULL_SIZE); initComponents(); centerWindow(this); initSearchManager(); } + public void showResult(String flag) { + cardLayout.show(resultPane, flag); + } + + public void addResult(JPanel panel, String flag) { + resultPane.add(panel, flag); + } + + public void removeSearchResultPane(JPanel panel) { + resultPane.remove(panel); + } + + public String getSearchText() { + return searchTextField.getText(); + } + + public CellType getSelectedType() { + return selectedTab.getCellType(); + } + private void initSearchManager() { this.productNewsSearchWorkerManager = new ProductNewsSearchWorkerManager( @@ -213,20 +252,26 @@ public class AlphaFineFrame extends JFrame { new LoadingRightSearchResultPane() ); + templateResourceSearchWorkerManager = new TemplateResourceSearchWorkerManager( + CellType.TEMPLATE_SHOP, + searchTextBean -> { + return TemplateResourceSearchManager.getInstance().getSearchResult(searchTextBean.getSearchText()); + }, + this + ); + } /** * 初始化全部组件 */ private void initComponents() { - add(createTopPane(), BorderLayout.NORTH); initSearchTextField(); add(createSearchPane(), BorderLayout.CENTER); add(createShowPane(), BorderLayout.SOUTH); this.getContentPane().setBackground(Color.WHITE); - this.setIconImage(SEARCH_IMAGE); - this.setSize(AlphaFineConstants.FULL_SIZE); + this.setIconImage(SEARCH_IMAGE); // 应用图标 } private JPanel createTopPane() { @@ -289,7 +334,6 @@ public class AlphaFineFrame extends JFrame { } }; - private JPopupMenu createTipPop() { JPanel panel = new JPanel(new BorderLayout()); String toolTip = AlphaFineShortCutUtil.getDisplayShortCut(com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Report_AlphaFine_Short_Cut", DesignerEnvManager.getEnvManager().getAlphaFineConfigManager().getShortcuts())); @@ -372,29 +416,35 @@ public class AlphaFineFrame extends JFrame { return searchPane; } + /** + * showPane,内容展示区,分为三个小区,tab区,label区,内容区 + * */ private JPanel createShowPane() { JPanel showPane = new JPanel(new BorderLayout()); + + // 内容区,card layout resultPane.add(new DefaultProductNewsPane(), CellType.PRODUCT_NEWS.getFlagStr4None()); resultPane.add(new NoResultWithLinkPane(GO_FORUM, AlphaFineConstants.NO_RESULT_ICON), CellType.NO_RESULT.getFlagStr4None()); resultPane.add(new NoResultPane(SEARCH_TERM, AlphaFineConstants.NO_RESULT_ICON), CellType.ACTION.getFlagStr4None()); resultPane.add(new NoResultPane(SEARCH_TERM, AlphaFineConstants.NO_RESULT_ICON), CellType.FILE.getFlagStr4None()); resultPane.add(new NoResultPane(SEARCH_TERM, AlphaFineConstants.NO_RESULT_ICON), CellType.PLUGIN.getFlagStr4None()); resultPane.add(new HelpDocumentNoResultPane(SEARCH_TERM, AlphaFineConstants.NO_RESULT_ICON), CellType.DOCUMENT.getFlagStr4None()); + resultPane.add(TemplateShopPane.getInstance(), CellType.TEMPLATE_SHOP.getFlagStr4None()); resultPane.add(new NetWorkFailedPane(this::reSearch), AlphaFineConstants.NETWORK_ERROR); - JPanel labelPane = new JPanel(new BorderLayout()); + + // label区,border layout + labelPane = new JPanel(new BorderLayout()); labelPane.setBorder(BorderFactory.createEmptyBorder(0, 20, 0, 20)); labelPane.setBackground(Color.WHITE); - JPanel labelContentPane = new JPanel(new BorderLayout()); - UILabel tabLabel = new UILabel(PRODUCT_DYNAMICS); - tabLabel.setForeground(AlphaFineConstants.FOREGROUND_COLOR_6); - tabLabel.setBorder(BorderFactory.createEmptyBorder(0, 10, 0, 0)); - tabLabel.setPreferredSize(new Dimension(100, 30)); - JPanel westPane = new JPanel(new FlowLayout(FlowLayout.LEFT, 0, 0)); - westPane.add(tabLabel); - labelContentPane.add(westPane, BorderLayout.WEST); - JPanel eastPane = new JPanel(new FlowLayout(FlowLayout.RIGHT, 0, 0)); - UILabel readLabel = new UILabel(ONE_CLICK_READ); + labelContentPane = new JPanel(new BorderLayout()); + tabLabel = createTabLabel(PRODUCT_NEWS); + labelWestPane = new JPanel(new FlowLayout(FlowLayout.LEFT, 0, 0)); + labelWestPane.add(tabLabel); + labelContentPane.add(labelWestPane, BorderLayout.WEST); + labelEastPane = new JPanel(new FlowLayout(FlowLayout.RIGHT, 0, 0)); + // 一键已读 + readLabel = new UILabel(ONE_CLICK_READ); readLabel.setHorizontalAlignment(SwingConstants.RIGHT); readLabel.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 10));; readLabel.setPreferredSize(new Dimension(100, 30)); @@ -406,24 +456,30 @@ public class AlphaFineFrame extends JFrame { showPane.repaint(); } }); - eastPane.add(readLabel); - labelContentPane.add(eastPane, BorderLayout.EAST); + labelEastPane.add(readLabel); + labelContentPane.add(labelEastPane, BorderLayout.EAST); labelContentPane.setBackground(new Color(245, 245, 247)); labelPane.add(labelContentPane); labelPane.setPreferredSize(new Dimension(AlphaFineConstants.FULL_SIZE.width, 30)); + // tab区 flow layout tabPane = new JPanel(new FlowLayout(FlowLayout.LEFT, 20, 10)); tabPane.setBackground(Color.WHITE); List selectedLabelList = createSelectedLabelList(); - selectedType = selectedLabelList.get(0).getCellType(); - // 第一个tab 非产品动态 - if (selectedType != CellType.PRODUCT_NEWS) { - tabLabel.setText(selectedLabelList.get(0).getText()); + selectedTab = null; + for (SelectedLabel label : selectedLabelList) { + if (label.isSelected()) { + selectedTab = label; + break; + } + } + if (this.selectedTab.getCellType() != CellType.PRODUCT_NEWS) { + tabLabel.setText(this.selectedTab.getText()); readLabel.setVisible(false); } - for (SelectedLabel selectedLabel : selectedLabelList) { - selectedLabel.addMouseListener(createMouseListener(selectedLabelList, selectedLabel, tabPane, tabLabel, readLabel)); - tabPane.add(selectedLabel); + for (SelectedLabel label : selectedLabelList) { + label.addMouseListener(createMouseListener(selectedLabelList, label, tabPane, tabLabel, readLabel)); + tabPane.add(label); } showPane.add(tabPane, BorderLayout.NORTH); showPane.add(labelPane, BorderLayout.CENTER); @@ -431,46 +487,55 @@ public class AlphaFineFrame extends JFrame { return showPane; } + private UILabel createTabLabel(String labelName) { + UILabel label = new UILabel(labelName); + label.setBorder(BorderFactory.createEmptyBorder(0, 10, 0, 0)); + label.setPreferredSize(new Dimension(60, 30)); + label.setForeground(AlphaFineConstants.LABEL_SELECTED); + return label; + } + + public JPanel getLabelWestPane() { + return labelWestPane; + } + + public UILabel getTabLabel() { + return tabLabel; + } + private MouseAdapter createMouseListener(List selectedLabelList, SelectedLabel selectedLabel, JPanel tabPane, UILabel tabLabel, UILabel readLabel) { return new MouseAdapter() { @Override public void mousePressed(MouseEvent e) { + // tab栏里的label全设置为没选中,并设置颜色为灰色 for (SelectedLabel label : selectedLabelList) { label.setSelected(false); label.setForeground(AlphaFineConstants.FOREGROUND_COLOR_8); } + // 选中 selectedLabel.setSelected(true); + AlphaFineFrame.this.selectedTab = selectedLabel; + // 处理产品动态 tab与下方文字展示不一致 if (ComparatorUtils.equals(selectedLabel.getText().trim(), PRODUCT_NEWS)) { - tabLabel.setText(PRODUCT_DYNAMICS); + tabLabel.setText(PRODUCT_NEWS); } else { tabLabel.setText(selectedLabel.getText()); } + + // 刷新westlabelpane + refreshLabelPane(); + + // 将已读设置不可见 readLabel.setVisible(false); + + // tab栏重新绘制 tabPane.repaint(); - switch (selectedLabel.getCellType()) { - case PRODUCT_NEWS: - readLabel.setVisible(true); - switchType(CellType.PRODUCT_NEWS); - break; - case ACTION: - currentSearchWorkerManager = settingSearchWorkerManager; - switchType(CellType.ACTION); - break; - case FILE: - currentSearchWorkerManager = fileSearchWorkerManager; - switchType(CellType.FILE); - break; - case DOCUMENT: - currentSearchWorkerManager = documentWorkerManager; - switchType(CellType.DOCUMENT); - break; - case PLUGIN: - currentSearchWorkerManager = pluginSearchWorkerManager; - switchType(CellType.PLUGIN); - break; - } + + // 选中事件 + switchTab(selectedLabel.getCellType(), readLabel); + if (currentSearchWorkerManager != null) { AlphaFineList alphaFineList = currentSearchWorkerManager.getSearchResultList(); if (alphaFineList != null) { @@ -494,17 +559,52 @@ public class AlphaFineFrame extends JFrame { }; } + + // 方便记埋点 + private void switchTab(CellType cellType, UILabel readLabel) { + switch (cellType) { + case PRODUCT_NEWS: + readLabel.setVisible(true); + switchType(CellType.PRODUCT_NEWS); + break; + case ACTION: + currentSearchWorkerManager = settingSearchWorkerManager; + switchType(CellType.ACTION); + break; + case FILE: + currentSearchWorkerManager = fileSearchWorkerManager; + switchType(CellType.FILE); + break; + case DOCUMENT: + currentSearchWorkerManager = documentWorkerManager; + switchType(CellType.DOCUMENT); + break; + case PLUGIN: + currentSearchWorkerManager = pluginSearchWorkerManager; + switchType(CellType.PLUGIN); + break; + case TEMPLATE_SHOP: + TemplateShopPane.getInstance().showPagePane(); + switchType(CellType.TEMPLATE_SHOP); + break; + } + } + + private void refreshLabelPane() { + labelWestPane.removeAll(); + tabLabel.setForeground(AlphaFineConstants.LABEL_SELECTED); + labelWestPane.add(tabLabel); + } + private List createSelectedLabelList() { List selectedLabelList = new ArrayList<>(); AlphaFineConfigManager alphaFineConfigManager = DesignerEnvManager.getEnvManager().getAlphaFineConfigManager(); if (alphaFineConfigManager.isProductDynamics()) { - selectedLabelList.add(new SelectedLabel(PRODUCT_NEWS, CellType.PRODUCT_NEWS, true)); - } - if (alphaFineConfigManager.isContainAction()) { - selectedLabelList.add(new SelectedLabel(SETTING, CellType.ACTION)); + selectedLabelList.add(new SelectedLabel(PRODUCT_NEWS, CellType.PRODUCT_NEWS)); } - if (alphaFineConfigManager.isContainFileContent() || alphaFineConfigManager.isContainTemplate()) { - selectedLabelList.add(new SelectedLabel(TEMPLATES, CellType.FILE)); + // 默认选中模板商城 + if (alphaFineConfigManager.hasTemplateShop()) { + selectedLabelList.add(new SelectedLabel(TEMPLATE_SHOP, CellType.TEMPLATE_SHOP, true)); } if (alphaFineConfigManager.isContainDocument()) { selectedLabelList.add(new SelectedLabel(HELP, CellType.DOCUMENT)); @@ -512,6 +612,19 @@ public class AlphaFineFrame extends JFrame { if (alphaFineConfigManager.isContainPlugin()) { selectedLabelList.add(new SelectedLabel(PLUGIN, CellType.PLUGIN)); } + if (alphaFineConfigManager.isContainAction()) { + selectedLabelList.add(new SelectedLabel(FUNCTION, CellType.ACTION)); + } + if (alphaFineConfigManager.isContainMyTemplate()) { + selectedLabelList.add(new SelectedLabel(MY_TEMPLATES, CellType.FILE)); + } + + + // 如果不展示模板商城,则list中第一个被选中 + if (!alphaFineConfigManager.hasTemplateShop() && !selectedLabelList.isEmpty()) { + selectedLabelList.get(0).setSelected(true); + } + return selectedLabelList; } @@ -524,7 +637,6 @@ public class AlphaFineFrame extends JFrame { } private void switchType(CellType cellType) { - this.selectedType = cellType; if (StringUtils.isEmpty(searchTextField.getText())) { cardLayout.show(resultPane, cellType.getFlagStr4None()); } else { @@ -541,8 +653,7 @@ public class AlphaFineFrame extends JFrame { if (checkNetworkError()) { return; } - - cardLayout.show(resultPane, cellType.getFlagStr4Result()); + showResult(cellType.getFlagStr4Result()); checkSearchResult(); } @@ -550,23 +661,27 @@ public class AlphaFineFrame extends JFrame { private boolean checkNetworkError() { boolean networkError; - if (selectedType == CellType.PRODUCT_NEWS) { + if (selectedTab.getCellType() == CellType.PRODUCT_NEWS) { networkError = productNewsSearchWorkerManager.isNetWorkError(); + } else if (selectedTab.getCellType() == CellType.TEMPLATE_SHOP) { + networkError = templateResourceSearchWorkerManager.isNetWorkError(); } else { networkError = currentSearchWorkerManager.isNetWorkError(); } - cardLayout.show(resultPane, AlphaFineConstants.NETWORK_ERROR); + showResult(AlphaFineConstants.NETWORK_ERROR); return networkError; } private boolean checkSearchLoading() { boolean searchOver; - if (selectedType == CellType.PRODUCT_NEWS) { + if (selectedTab.getCellType() == CellType.PRODUCT_NEWS) { searchOver = productNewsSearchWorkerManager.isSearchOver(); + } else if (selectedTab.getCellType() == CellType.TEMPLATE_SHOP) { + searchOver = templateResourceSearchWorkerManager.isSearchOver(); } else { searchOver = currentSearchWorkerManager.isSearchOver(); } - cardLayout.show(resultPane, AlphaFineConstants.LOADING); + showResult(AlphaFineConstants.LOADING); return searchOver; } @@ -586,17 +701,20 @@ public class AlphaFineFrame extends JFrame { if (searchResultList != null) { searchResultList.requestFocus(); } - boolean hasSearchResult = true; - if (selectedType == CellType.PRODUCT_NEWS) { - hasSearchResult = productNewsSearchWorkerManager.hasSearchResult(); - } else { - hasSearchResult = currentSearchWorkerManager.hasSearchResult(); - } - if (!hasSearchResult) { - cardLayout.show(resultPane, CellType.NO_RESULT.getFlagStr4None()); + if (!hasSearchResult()) { + showResult(CellType.NO_RESULT.getFlagStr4None()); } + } + private boolean hasSearchResult() { + if (selectedTab.getCellType() == CellType.PRODUCT_NEWS) { + return productNewsSearchWorkerManager.hasSearchResult(); + } else if (selectedTab.getCellType() == CellType.TEMPLATE_SHOP) { + return templateResourceSearchWorkerManager.hasSearchResult(); + } else { + return currentSearchWorkerManager.hasSearchResult(); + } } private void initSearchTextField() { @@ -608,7 +726,6 @@ public class AlphaFineFrame extends JFrame { searchTextField.setBorder(null); } - private void initTextFieldListener() { searchTextField.addKeyListener(new KeyAdapter() { @Override @@ -659,20 +776,24 @@ public class AlphaFineFrame extends JFrame { } + /** + * 控制搜索tip框弹出收起 + * 不断地刷新tab页,并防止tab页显示错误 + * */ private void startSearchTextFieldTimer() { Timer timer = new Timer(TIMER_DELAY, e -> { // 坑 isShowing返回false 即使textField有内容 getText返回的也是空 if (searchTextField.isShowing() && StringUtils.isEmpty(searchTextField.getText())) { SearchTooltipPopup.getInstance().hide(); clearLabel.setVisible(false); - switchType(selectedType); + switchType(selectedTab.getCellType()); + TemplateShopPane.getInstance().quitSearchResultPane(); beforeSearchStr = StringUtils.EMPTY; } else if (searchTextField.hasFocus()) { clearLabel.setVisible(true); SearchTooltipPopup.getInstance().show(searchTextFieldWrapperPane); } tabPane.repaint(); - }); timer.start(); } @@ -751,21 +872,8 @@ public class AlphaFineFrame extends JFrame { } } - public void showResult(String flag) { - cardLayout.show(resultPane, flag); - } - - public void addResult(JPanel panel, String flag) { - resultPane.add(panel, flag); - } - - public void removeSearchResultPane(JPanel panel) { - resultPane.remove(panel); - } - - - private void doSearch(String text) { + refreshLabelPane(); initSearchLoadingPane(); SearchTextBean searchTextBean = generateSearchTextBean(text); this.productNewsSearchWorkerManager.doSearch(searchTextBean); @@ -773,6 +881,7 @@ public class AlphaFineFrame extends JFrame { this.fileSearchWorkerManager.doSearch(searchTextBean); this.documentWorkerManager.doSearch(searchTextBean); this.pluginSearchWorkerManager.doSearch(searchTextBean); + this.templateResourceSearchWorkerManager.doSearch(searchTextBean); } private SearchTextBean generateSearchTextBean(String searchText) { @@ -806,25 +915,15 @@ public class AlphaFineFrame extends JFrame { this.pluginSearchWorkerManager.doSearch(searchTextBean); } + /** + * 所有tab页搜索通用的加载panel + * */ private void initSearchLoadingPane() { if (searchLoadingPane == null) { searchLoadingPane = new SearchLoadingPane(); } resultPane.add(searchLoadingPane, AlphaFineConstants.LOADING); - cardLayout.show(resultPane, AlphaFineConstants.LOADING); - } - - public String getSearchText() { - return searchTextField.getText(); - } - - - public CellType getSelectedType() { - return selectedType; - } - - public void setStoreText(String storeText) { - this.storeText = storeText; + showResult(AlphaFineConstants.LOADING); } /** @@ -839,6 +938,9 @@ public class AlphaFineFrame extends JFrame { return storeText; } + public void setStoreText(String storeText) { + this.storeText = storeText; + } /** * 去除特殊字符,空格等 @@ -894,6 +996,7 @@ public class AlphaFineFrame extends JFrame { @Override public void setVisible(boolean b) { super.setVisible(b); + switchTab(selectedTab.getCellType(), readLabel); QuestionWindow.getInstance().setVisible(!b); if (!b) { AlphaFineHelper.resetAlphaFineDialog(); diff --git a/designer-realize/src/main/java/com/fr/design/mainframe/alphafine/component/ProductNewsList.java b/designer-realize/src/main/java/com/fr/design/mainframe/alphafine/component/ProductNewsList.java index 1a8e0a2a2..48dfa4b86 100644 --- a/designer-realize/src/main/java/com/fr/design/mainframe/alphafine/component/ProductNewsList.java +++ b/designer-realize/src/main/java/com/fr/design/mainframe/alphafine/component/ProductNewsList.java @@ -5,18 +5,15 @@ import com.fr.design.mainframe.alphafine.AlphaFineConstants; import com.fr.design.mainframe.alphafine.AlphaFineHelper; import com.fr.design.mainframe.alphafine.model.ProductNews; import com.fr.design.utils.BrowseUtils; -import com.fr.log.FineLoggerFactory; +import javax.swing.DefaultListModel; +import javax.swing.JList; +import javax.swing.ListModel; import java.awt.Cursor; -import java.awt.Desktop; import java.awt.Point; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.awt.event.MouseMotionAdapter; -import java.net.URI; -import javax.swing.DefaultListModel; -import javax.swing.JList; -import javax.swing.ListModel; /** * @author hades @@ -64,11 +61,16 @@ public class ProductNewsList extends JList { private void dealWithClick() { ProductNews productNews = getSelectedValue(); - BrowseUtils.browser(productNews.getUrl()); + openNewsInBrowser(productNews.getUrl()); DesignerEnvManager.getEnvManager().getAlphaFineConfigManager().getReadSet().add(productNews.getId()); AlphaFineHelper.getAlphaFineDialog().repaint(); } + // 方便埋点 + private void openNewsInBrowser(String url) { + BrowseUtils.browser(url); + } + public int getHoverIndex() { return hoverIndex; } diff --git a/designer-realize/src/main/java/com/fr/design/mainframe/alphafine/component/RecommendSearchPane.java b/designer-realize/src/main/java/com/fr/design/mainframe/alphafine/component/RecommendSearchPane.java new file mode 100644 index 000000000..a0b0ef4bf --- /dev/null +++ b/designer-realize/src/main/java/com/fr/design/mainframe/alphafine/component/RecommendSearchPane.java @@ -0,0 +1,74 @@ +package com.fr.design.mainframe.alphafine.component; + +import com.fr.design.i18n.Toolkit; +import com.fr.design.mainframe.alphafine.AlphaFineHelper; +import com.fr.design.mainframe.alphafine.model.TemplateResource; + +import javax.swing.BorderFactory; +import javax.swing.JLabel; +import javax.swing.JPanel; +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.FlowLayout; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.util.List; + +public class RecommendSearchPane extends TemplateResourcePanel { + + private static final Color BORDER_WHITE = new Color(0xe8e8e9); + private static final Color RECOMMEND_SEARCH_KEY_BLUE = new Color(0x419bf9); + + public RecommendSearchPane(TemplateResource templateResource) { + super(); + setTemplateResource(templateResource); + initComponent(); + this.setLayout(new BorderLayout()); + + this.setBorder(BorderFactory.createLineBorder(BORDER_WHITE, 1)); + this.add(getNorthPane(), BorderLayout.NORTH); + this.add(getSouthPane(), BorderLayout.SOUTH); + } + + private void initComponent() { + createNorthPane(); + createSouthPane(); + } + + + private void createSouthPane() { + setSouthPane(new JPanel(new FlowLayout(FlowLayout.LEFT))); + JPanel southPane = getSouthPane(); + southPane.setBackground(Color.WHITE); + JLabel recommend = new JLabel(Toolkit.i18nText("Fine-Design_Report_AlphaFine_Template_Resource_Recommend_For_You")); + southPane.add(recommend); + + List searchKeys = getTemplateResource().getRecommendSearchKey(); + + for (String key : searchKeys) { + JLabel keyLabel = new SearchKeyLabel(key); + southPane.add(keyLabel); + } + } + + + class SearchKeyLabel extends JLabel { + String searchKey; + + SearchKeyLabel(String searchKey) { + this.searchKey = searchKey; + setText(searchKey); + setBackground(Color.WHITE); + setForeground(RECOMMEND_SEARCH_KEY_BLUE); + addMouseListener(new MouseAdapter() { + @Override + public void mouseClicked(MouseEvent e) { + AlphaFineHelper.getAlphaFineDialog().fireSearch(searchKey); + } + }); + } + + + } + +} \ No newline at end of file diff --git a/designer-realize/src/main/java/com/fr/design/mainframe/alphafine/component/TemplateResourceImagePanel.java b/designer-realize/src/main/java/com/fr/design/mainframe/alphafine/component/TemplateResourceImagePanel.java new file mode 100644 index 000000000..13d29d76f --- /dev/null +++ b/designer-realize/src/main/java/com/fr/design/mainframe/alphafine/component/TemplateResourceImagePanel.java @@ -0,0 +1,74 @@ +package com.fr.design.mainframe.alphafine.component; + +import com.fr.base.GraphHelper; +import com.fr.design.mainframe.alphafine.model.TemplateResource; +import com.fr.third.jodd.util.StringUtil; + +import javax.swing.JPanel; +import java.awt.Color; +import java.awt.Dimension; +import java.awt.Graphics; +import java.awt.Graphics2D; +import java.awt.Image; +import java.awt.RenderingHints; + + +public class TemplateResourceImagePanel extends JPanel { + + private static final int BACKGROUND_HEIGHT = 20; + + private static final Color BACKGROUND_COLOR = new Color(116, 181, 249); + + private static final Color COVER_COLOR = new Color(116, 181, 249, 26); + + private TemplateResource templateResource; + + private int width = 200; + private int height = 100; + + public TemplateResourceImagePanel(TemplateResource templateResource) { + this.templateResource = templateResource; + } + + public TemplateResourceImagePanel(TemplateResource templateResource, int width, int height) { + this(templateResource); + this.width = width; + this.height = height; + } + + + @Override + protected void paintComponent(Graphics g) { + super.paintComponent(g); + Graphics2D g2 = (Graphics2D) g; + g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); + g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR); + Color defaultColor = g2.getColor(); + + Image image = templateResource.getImage(); + if (image != null) { + g2.drawImage(templateResource.getImage(), 0, 0, getWidth(), getHeight(), this); + } else { + g2.setColor(COVER_COLOR); + g2.fillRect(0, 0, getWidth(), getHeight()); + } + + String tagName = templateResource.getType().getName(); + + if (!StringUtil.isEmpty(tagName)) { + g2.setColor(BACKGROUND_COLOR); + g2.fillRect(0, getHeight() - BACKGROUND_HEIGHT, getWidth(), BACKGROUND_HEIGHT); + g2.setColor(Color.WHITE); + int x = (getWidth() - GraphHelper.getWidth(tagName, g2.getFont())) / 2; + g2.drawString(tagName, x, getHeight() - 5); + } + g2.setColor(defaultColor); + } + + @Override + public Dimension getPreferredSize() { + return new Dimension(width, height); + } + + +} \ No newline at end of file diff --git a/designer-realize/src/main/java/com/fr/design/mainframe/alphafine/component/TemplateResourcePageGridPane.java b/designer-realize/src/main/java/com/fr/design/mainframe/alphafine/component/TemplateResourcePageGridPane.java new file mode 100644 index 000000000..2bafebc56 --- /dev/null +++ b/designer-realize/src/main/java/com/fr/design/mainframe/alphafine/component/TemplateResourcePageGridPane.java @@ -0,0 +1,225 @@ +package com.fr.design.mainframe.alphafine.component; + +import com.fr.base.svg.IconUtils; +import com.fr.design.gui.icontainer.UIScrollPane; +import com.fr.design.layout.TableLayoutHelper; +import com.fr.design.mainframe.alphafine.model.TemplateResource; + +import javax.swing.BorderFactory; +import javax.swing.JButton; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JTextField; +import java.awt.BorderLayout; +import java.awt.CardLayout; +import java.awt.Color; +import java.awt.Component; +import java.awt.Dimension; +import java.awt.FlowLayout; +import java.awt.Label; +import java.awt.event.KeyAdapter; +import java.awt.event.KeyEvent; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +/** + * 卡片布局,每个卡片里塞了scrollpanel + * */ +public class TemplateResourcePageGridPane extends JPanel { + + private List data; + private CardLayout cardLayout; + private List pages; + private int totalPage; + + List scrollPanes = new ArrayList<>(); + + private static final int PAGE_MAX_SIZE = 12; + private static final int TABLE_MAX_ROW_COUNT = 4; + private static final int TABLE_COL_COUNT = 3; + private static final int TABLE_VGAP = 15; + private static final int TABLE_HGAP = 15; + private static final int RESOURCE_WIDTH = 197; + private static final int RESOURCE_HEIGHT = 128; + + public TemplateResourcePageGridPane(List templateResourceList) { + this.data = templateResourceList; + totalPage = (int) Math.ceil((double)data.size() / PAGE_MAX_SIZE); + createPages(); + initComponents(); + this.setBackground(Color.WHITE); + this.setBorder(BorderFactory.createEmptyBorder(10, 20, 0, 20)); + switchPage(1); + } + + private void initComponents() { + cardLayout = new CardLayout(); + this.setLayout(cardLayout); + this.setBackground(Color.WHITE); + for (int i = 0; i < pages.size(); i++) { + UIScrollPane scrollPane = new UIScrollPane(pages.get(i)); + scrollPanes.add(scrollPane); + this.add(scrollPane, String.valueOf(i + 1)); + } + } + + + /** + * 构建分页,将资源切分到每一页,并在每一页尾部添加分页按钮 + * */ + private void createPages() { + int dataCnt = data.size(); + List[] slice = new ArrayList[totalPage]; + for (int i = 0; i < dataCnt; i++) { + int index = i / PAGE_MAX_SIZE; + if (slice[index] == null) { + slice[index] = new ArrayList<>(); + } + slice[index].add(data.get(i)); + } + pages = new ArrayList<>(); + for (int i = 0; i < totalPage; i++) { + pages.add(new Page(slice[i], i + 1)); + } + } + + + private void switchPage(int pageNumber) { + if (pageNumber < 1 || pageNumber > this.totalPage) { + return; + } + cardLayout.show(TemplateResourcePageGridPane.this, String.valueOf(pageNumber)); + scrollPanes.get(pageNumber - 1).getVerticalScrollBar().setValue(0); + // 坑,切换页面会刷新失败,需要手动滚动一下才能刷新 + scrollPanes.get(pageNumber - 1).getVerticalScrollBar().setValue(1); + scrollPanes.get(pageNumber - 1).getVerticalScrollBar().setValue(0); + } + + + /** + * 分页panel,borderlayout布局,north为信息页,south为分页按钮区 + * */ + private class Page extends JPanel { + List pageData; + Component[][] comps; + + JPanel contentPane; + + JPanel pageButtonPane; + JButton prev, next; + JTextField pageNumberField; + JLabel pageCnt; + + int pageNumber; + + Page(List pageData, int pageNumber) { + super(); + this.pageData = pageData; + this.pageNumber = pageNumber; + initComponents(); + this.setLayout(new BorderLayout()); + this.add(contentPane, BorderLayout.NORTH); + if (totalPage > 1) { + this.add(pageButtonPane, BorderLayout.SOUTH); + } + this.setBackground(Color.WHITE); + } + + private void initComponents() { + createContentPane(); + createPageButtonPane(); + } + + void createContentPane() { + int dataCnt = pageData.size(); + int rowCnt = (int) Math.ceil((double)dataCnt / 3); + double[] rowHeight = new double[rowCnt]; + double[] colWidth = new double[TABLE_COL_COUNT]; + Arrays.fill(rowHeight, RESOURCE_HEIGHT); + Arrays.fill(colWidth, RESOURCE_WIDTH); + comps = new Component[rowCnt][TABLE_COL_COUNT]; + + for (int i = 0; i < rowCnt; i++) { + for (int j = 0; j < TABLE_COL_COUNT; j++) { + int which = i * 3 + j; + if (which >= dataCnt) { + Label empty = new Label(); + empty.setPreferredSize(new Dimension(RESOURCE_WIDTH, RESOURCE_HEIGHT)); + empty.setVisible(false); + comps[i][j] = empty; + } else { + TemplateResourcePanel resource = TemplateResourcePanel.create(pageData.get(which)); + resource.setPreferredSize(new Dimension(RESOURCE_WIDTH, RESOURCE_HEIGHT)); + comps[i][j] = resource; + } + } + } + contentPane = TableLayoutHelper.createGapTableLayoutPane(comps, rowHeight, colWidth, TABLE_HGAP, TABLE_VGAP); + contentPane.setBackground(Color.WHITE); + } + + void createPageButtonPane() { + prev = new JButton(IconUtils.readIcon("/com/fr/design/mainframe/alphafine/images/prev.svg")); + next = new JButton(IconUtils.readIcon("/com/fr/design/mainframe/alphafine/images/next.svg")); + pageNumberField = new JTextField((int) Math.log10(totalPage) + 1); + pageNumberField.setText(String.valueOf(this.pageNumber)); + pageCnt = new JLabel("/ " + totalPage); + + pageButtonPane = new JPanel(new FlowLayout(FlowLayout.RIGHT)); + pageButtonPane.add(prev); + pageButtonPane.add(pageNumberField); + pageButtonPane.add(pageCnt); + pageButtonPane.add(next); + + addPageAction(); + } + + // 添加翻页按钮事件 + void addPageAction() { + addPrevPageAction(); + addNextPageAction(); + addGotoPageAction(); + } + + void addPrevPageAction() { + prev.addMouseListener(new MouseAdapter() { + @Override + public void mouseClicked(MouseEvent e) { + super.mouseClicked(e); + if (pageNumber > 1) { + switchPage(pageNumber - 1); + } + } + }); + }; + void addNextPageAction() { + next.addMouseListener(new MouseAdapter() { + @Override + public void mouseClicked(MouseEvent e) { + super.mouseClicked(e); + if (pageNumber < totalPage) { + switchPage(pageNumber + 1); + } + } + }); + + } + void addGotoPageAction() { + pageNumberField.addKeyListener(new KeyAdapter() { + @Override + public void keyPressed(KeyEvent e) { + super.keyPressed(e); + String numb = pageNumberField.getText(); + if (numb != null && !numb.equals(pageNumber)) { + if (e.getKeyCode() == KeyEvent.VK_ENTER) { + switchPage(Integer.parseInt(numb)); + } + } + } + }); + } + } +} \ No newline at end of file diff --git a/designer-realize/src/main/java/com/fr/design/mainframe/alphafine/component/TemplateResourcePanel.java b/designer-realize/src/main/java/com/fr/design/mainframe/alphafine/component/TemplateResourcePanel.java new file mode 100644 index 000000000..4eba18c63 --- /dev/null +++ b/designer-realize/src/main/java/com/fr/design/mainframe/alphafine/component/TemplateResourcePanel.java @@ -0,0 +1,122 @@ +package com.fr.design.mainframe.alphafine.component; + +import com.fr.design.i18n.Toolkit; +import com.fr.design.mainframe.alphafine.model.TemplateResource; +import com.fr.design.mainframe.alphafine.preview.TemplateShopPane; +import com.fr.design.utils.BrowseUtils; +import com.fr.log.FineLoggerFactory; + +import javax.swing.BorderFactory; +import javax.swing.JLabel; +import javax.swing.JPanel; +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.Dimension; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; + + +public class TemplateResourcePanel extends JPanel { + + private JPanel northPane; + private JPanel southPane; + private TemplateResource templateResource; + + private static final Color PANEL_BORDER_COLOR = new Color(0xe8e8e9); + private static final Color DEMO_LABEL_FOREGROUND = new Color(0x419bf9); + + protected TemplateResourcePanel() { + + } + + protected TemplateResourcePanel(TemplateResource templateResource) { + this.templateResource = templateResource; + initComponent(); + this.setLayout(new BorderLayout()); + this.setBorder(BorderFactory.createLineBorder(PANEL_BORDER_COLOR, 1)); + this.add(northPane, BorderLayout.NORTH); + this.add(southPane, BorderLayout.SOUTH); + addAction(); + } + + private void addAction() { + this.addMouseListener(new MouseAdapter() { + @Override + public void mouseClicked(MouseEvent e) { + super.mouseClicked(e); + TemplateShopPane.getInstance().searchAndShowDetailPane(templateResource); + } + }); + } + + public static TemplateResourcePanel create(TemplateResource templateResource) { + if (TemplateResource.Type.RECOMMEND_SEARCH.equals(templateResource.getType())) { + return new RecommendSearchPane(templateResource); + } else { + return new TemplateResourcePanel(templateResource); + } + } + + public JPanel getNorthPane() { + return northPane; + } + public JPanel getSouthPane() { + return southPane; + } + public TemplateResource getTemplateResource() { + return templateResource; + } + + public void setNorthPane(JPanel northPane) { + this.northPane = northPane; + } + + public void setSouthPane(JPanel southPane) { + this.southPane = southPane; + } + + public void setTemplateResource(TemplateResource templateResource) { + this.templateResource = templateResource; + } + + private void initComponent() { + createNorthPane(); + createSouthPane(); + } + + protected void createNorthPane() { + northPane = new TemplateResourceImagePanel(templateResource); + } + + private void createSouthPane() { + JLabel nameLabel = new JLabel(templateResource.getName()); + nameLabel.setBackground(Color.WHITE); + nameLabel.setBorder(BorderFactory.createEmptyBorder()); + + JLabel demoLabel = new JLabel(); + if (templateResource.hasDemoUrl()) { + demoLabel.setText(Toolkit.i18nText("Fine-Design_Report_AlphaFine_Template_Resource_Demo")); + demoLabel.setForeground(DEMO_LABEL_FOREGROUND); + demoLabel.addMouseListener(new MouseAdapter() { + @Override + public void mouseClicked(MouseEvent e) { + try { + BrowseUtils.browser(templateResource.getDemoUrl()); + } catch (Exception ex) { + FineLoggerFactory.getLogger().error(ex, ex.getMessage()); + } + } + }); + } + + southPane = new JPanel(new BorderLayout()); + southPane.setBackground(Color.WHITE); + southPane.add(nameLabel, BorderLayout.WEST); + southPane.add(demoLabel, BorderLayout.EAST); + } + + @Override + public Dimension getPreferredSize() { + return new Dimension(180, 90); + } +} \ No newline at end of file diff --git a/designer-realize/src/main/java/com/fr/design/mainframe/alphafine/download/FineMarketConstants.java b/designer-realize/src/main/java/com/fr/design/mainframe/alphafine/download/FineMarketConstants.java new file mode 100644 index 000000000..f17095b09 --- /dev/null +++ b/designer-realize/src/main/java/com/fr/design/mainframe/alphafine/download/FineMarketConstants.java @@ -0,0 +1,10 @@ +package com.fr.design.mainframe.alphafine.download; + +public class FineMarketConstants { + + public static final String REPORTLETS = "/reportlets"; + public static final String ZIP = ".zip"; + public static final String RAR = ".rar"; + public static final String CPT = ".cpt"; + public static final String FRM = ".frm"; +} diff --git a/designer-realize/src/main/java/com/fr/design/mainframe/alphafine/download/FineMarketDownloadManager.java b/designer-realize/src/main/java/com/fr/design/mainframe/alphafine/download/FineMarketDownloadManager.java new file mode 100644 index 000000000..08a785311 --- /dev/null +++ b/designer-realize/src/main/java/com/fr/design/mainframe/alphafine/download/FineMarketDownloadManager.java @@ -0,0 +1,149 @@ +package com.fr.design.mainframe.alphafine.download; + +import com.fr.base.svg.IconUtils; +import com.fr.common.util.Strings; +import com.fr.design.DesignerEnvManager; +import com.fr.design.extra.Process; +import com.fr.design.i18n.Toolkit; +import com.fr.design.login.DesignerLoginHelper; +import com.fr.design.login.DesignerLoginSource; +import com.fr.design.mainframe.alphafine.AlphaFineHelper; +import com.fr.design.mainframe.alphafine.model.TemplateResource; +import com.fr.design.mainframe.alphafine.search.helper.FineMarketClientHelper; +import com.fr.design.mainframe.toast.SimpleToast; +import com.fr.log.FineLoggerFactory; +import com.fr.third.jodd.io.ZipUtil; +import com.fr.workspace.WorkContext; + +import javax.swing.SwingUtilities; +import java.awt.Window; +import java.io.File; +import java.io.IOException; +import java.util.HashMap; + + +/** + * 在这里统一管理帆软市场的下载 + * 下载的流程控制尽量都在这个类内部完成 + * 通过Process类来实现下载流程控制 + * */ +public class FineMarketDownloadManager { + + private static final FineMarketDownloadManager INSTANCE = new FineMarketDownloadManager(); + public static final FineMarketDownloadManager getInstance() { + return INSTANCE; + } + + + public static final double PROCESS_SUCCESS = 1d; + public static final double PROCESS_FAILED = -1d; + public static final double OPENING_FILE = 2d; + + private static final String OPENING_PLEASE_WAIT = Toolkit.i18nText("Fine-Design_Report_AlphaFine_Template_Resource_Opening"); + private static final String DOWNLOAD_FAILED = Toolkit.i18nText("Fine-Design_Report_AlphaFine_Template_Resource_Download_Failed_Check_Network"); + + + /** + * 下载资源并解压 + * */ + public String installResource(TemplateResource resource, Window parentWindow){ + // 验证登录 + String token = DesignerEnvManager.getEnvManager().getDesignerLoginRefreshToken(); + if (Strings.isEmpty(token)) { + DesignerLoginHelper.showLoginDialog(DesignerLoginSource.NORMAL, new HashMap<>(), AlphaFineHelper.getAlphaFineDialog()); + return null; + } + return install(resource, parentWindow); + } + + private String install(TemplateResource resource, Window parentWindow) { + // 获取报表录作为下载位置 + String workDir = WorkContext.getCurrent().getPath() + FineMarketConstants.REPORTLETS; + File destDir = new File(workDir); + + DownloadProcess downloadProcess = new DownloadProcess(parentWindow); + String fileName = null; + try { + fileName = FineMarketClientHelper.getInstance().download(resource, destDir, downloadProcess); + unzip(fileName, downloadProcess); + return fileName; + } catch (Exception e) { + downloadProcess.process(FineMarketDownloadManager.PROCESS_FAILED); + FineLoggerFactory.getLogger().error(e, e.getMessage()); + } + return null; + } + + void unzip(String fileName, DownloadProcess process) throws IOException { + process.process(OPENING_FILE); + + if (fileName.endsWith(FineMarketConstants.ZIP)) { + File file = new File(fileName); + File parentDir = file.getParentFile(); + ZipUtil.unzip(file, parentDir); + } + } + + + + + /** + * 下载流程控制,主要控制ui的显示 + * */ + class DownloadProcess implements Process { + + SimpleToast downloadingToast; + SimpleToast openingToast; + SimpleToast failedToast; + Window parent; + + public DownloadProcess(Window parentWindow) { + this.parent = parentWindow; + init(); + } + + void init() { + showLoadingToast(); + } + + @Override + public void process(Double aDouble) { + SwingUtilities.invokeLater(()->{ + if (aDouble == PROCESS_FAILED) { + downloadFailed(); + } else if (aDouble == PROCESS_SUCCESS) { + downloadSuccess(); + } else if (aDouble == OPENING_FILE) { + openingFile(); + } + }); + } + + public void downloadFailed() { + downloadingToast.setVisible(false); + showFailedToast(); + } + + public void downloadSuccess() { + downloadingToast.setVisible(false); + } + + + private void openingFile() { + downloadingToast.setVisible(false); + openingToast = new SimpleToast(AlphaFineHelper.getAlphaFineDialog(), IconUtils.readIcon("/com/fr/design/mainframe/alphafine/images/loading.svg"), OPENING_PLEASE_WAIT, true); + openingToast.setVisible(true); + } + + private void showLoadingToast() { + downloadingToast = new SimpleToast(AlphaFineHelper.getAlphaFineDialog(), IconUtils.readIcon("/com/fr/design/mainframe/alphafine/images/loading.svg"), OPENING_PLEASE_WAIT, false); + downloadingToast.setVisible(true); + } + + private void showFailedToast() { + failedToast = new SimpleToast(AlphaFineHelper.getAlphaFineDialog(), IconUtils.readIcon("/com/fr/design/mainframe/alphafine/images/caution.svg"), DOWNLOAD_FAILED, true); + failedToast.setVisible(true); + } + + } +} diff --git a/designer-realize/src/main/java/com/fr/design/mainframe/alphafine/model/ProductNews.java b/designer-realize/src/main/java/com/fr/design/mainframe/alphafine/model/ProductNews.java index aea152da2..1673f3bb8 100644 --- a/designer-realize/src/main/java/com/fr/design/mainframe/alphafine/model/ProductNews.java +++ b/designer-realize/src/main/java/com/fr/design/mainframe/alphafine/model/ProductNews.java @@ -1,8 +1,12 @@ package com.fr.design.mainframe.alphafine.model; import com.fr.design.i18n.Toolkit; + import java.awt.Image; +import java.util.ArrayList; +import java.util.Arrays; import java.util.Date; +import java.util.List; /** * 产品动态 @@ -17,7 +21,9 @@ public class ProductNews { private String title; private Tag tag; - private Target target; + + // 推送对象,对象为 list<用户组id> + private List target; private Status status; private String url; @@ -26,6 +32,8 @@ public class ProductNews { private Date pushDate; + public static final String ALL_USER_TARGET = "0"; + /** * 创建cid的用户 */ @@ -58,11 +66,11 @@ public class ProductNews { return this; } - public Target getTarget() { + public List getTarget() { return target; } - public ProductNews setTarget(Target target) { + public ProductNews setTarget(List target) { this.target = target; return this; } @@ -172,29 +180,18 @@ public class ProductNews { } } - public enum Target { - ALL_USER(0); - - private final int code; - - Target(int code) { - this.code = code; - } - - public int getCode() { - return code; - } - - public static Target parseCode(int code) { - for (Target target : values()) { - if (target.code == code) { - return target; - } - } - throw new IllegalArgumentException(); + /** + * 将接口中的target字段转换一下 + * 原始数据是字符串,例如:"1,2,3,4" + * 转换为 List{1,2,3,4} + * */ + public static List ParseTarget(String s) { + List list = new ArrayList<>(); + if (s != null) { + String[] targets = s.split(","); + list.addAll(Arrays.asList(targets)); } - + return list; } - } diff --git a/designer-realize/src/main/java/com/fr/design/mainframe/alphafine/model/TemplateResource.java b/designer-realize/src/main/java/com/fr/design/mainframe/alphafine/model/TemplateResource.java new file mode 100644 index 000000000..56cc25a0c --- /dev/null +++ b/designer-realize/src/main/java/com/fr/design/mainframe/alphafine/model/TemplateResource.java @@ -0,0 +1,209 @@ +package com.fr.design.mainframe.alphafine.model; + +import com.fr.common.util.Strings; +import com.fr.design.i18n.Toolkit; +import com.fr.design.mainframe.alphafine.download.FineMarketConstants; +import com.fr.design.mainframe.alphafine.search.manager.impl.TemplateResourceSearchManager; +import com.fr.general.IOUtils; +import com.fr.json.JSONArray; +import com.fr.json.JSONObject; +import com.fr.third.jodd.util.StringUtil; + +import java.awt.Image; +import java.util.ArrayList; +import java.util.List; + + +/** + * 模板资源数据 + */ +public class TemplateResource { + + /*** + * 模板资源类型:模板,解决方案,推荐搜索 + */ + public enum Type { + SINGLE_TEMPLATE(Toolkit.i18nText("Fine-Design_Report_AlphaFine_Template_Resource_Single_Template")), + SCENARIO_SOLUTION(Toolkit.i18nText("Fine-Design_Report_AlphaFine_Template_Resource_Scenario_Solution")), + RECOMMEND_SEARCH; + + private String name; + + Type() { + } + + Type(String name) { + this.name = name; + } + + public String getName() { + return name; + } + } + + // 模板资源搜索接口返回值字段名 + public static final String ID = "id"; + public static final String UUID = "uuid"; + public static final String NAME = "name"; + public static final String IMAGE_URL = "pic"; + public static final String DEMO_URL = "demoUrl"; + public static final String PKG_SIZE = "pkgsize"; + public static final String FILE_NAME = "fileLoca"; + private static final String RECOMMEND_SEARCH_IMG_URL = "com/fr/design/mainframe/alphafine/images/more.png"; + + + // 模板资源属性 + private String id; + private String uuid; + private Type type; + private String imageUrl; + private Image image; + private String name; + private String demoUrl; + private String fileName; + private int pkgSize; + private List recommendSearchKey; + + public static List createByJson(JSONArray jsonArray) { + List list = new ArrayList<>(); + if (jsonArray != null) { + for (int i = jsonArray.length() - 1; i >= 0; i--) { + list.add(createByJson(jsonArray.getJSONObject(i))); + } + } + return list; + } + + + public static TemplateResource createByJson(JSONObject jsonObject) { + + TemplateResource templateResource = new TemplateResource().setId(jsonObject.getString(ID)).setUuid(jsonObject.getString(UUID)).setName(jsonObject.getString(NAME)) + .setDemoUrl(jsonObject.getString(DEMO_URL)).setPkgSize(jsonObject.getInt(PKG_SIZE)).setFileName(jsonObject.getString(FILE_NAME)); + int pkgSize = templateResource.getPkgSize(); + if (pkgSize == 0) { + templateResource.type = Type.SINGLE_TEMPLATE; + } else { + templateResource.type = Type.SCENARIO_SOLUTION; + } + + templateResource.setImageUrl(parseUrl(jsonObject)); + + templateResource.setImage(IOUtils.readImage(templateResource.imageUrl)); + return templateResource; + } + + /** + * 商城接口传过来的图片url是特殊格式,需要特殊处理下 + * */ + static String parseUrl(JSONObject jsonObject) { + String imgUrl = jsonObject.getString(IMAGE_URL); + int index = imgUrl.indexOf(","); + if (index != -1) { + imgUrl = imgUrl.substring(0, imgUrl.indexOf(",")); + } + return imgUrl; + } + + public static TemplateResource getRecommendSearch() { + TemplateResource recommend = new TemplateResource(); + recommend.setType(Type.RECOMMEND_SEARCH); + recommend.setImageUrl(RECOMMEND_SEARCH_IMG_URL); + recommend.setImage(IOUtils.readImage(RECOMMEND_SEARCH_IMG_URL)); + recommend.setRecommendSearchKey(TemplateResourceSearchManager.getInstance().getRecommendSearchKeys()); + return recommend; + } + + + + public String getFileName() { + return fileName; + } + + public TemplateResource setFileName(String fileName) { + if (Strings.isEmpty(fileName)) { + this.fileName = getName() + FineMarketConstants.ZIP; + } else { + this.fileName = fileName; + } + return this; + } + + public Type getType() { + return type; + } + + public void setType(Type type) { + this.type = type; + } + + public List getRecommendSearchKey() { + return recommendSearchKey; + } + + public void setRecommendSearchKey(List recommendSearchKey) { + this.recommendSearchKey = recommendSearchKey; + } + + public TemplateResource setImage(Image image) { + this.image = image; + return this; + } + + public Image getImage() { + return image; + } + + public TemplateResource setImageUrl(String imageUrl) { + this.imageUrl = imageUrl; + return this; + } + + public String getName() { + return name; + } + + public TemplateResource setName(String name) { + this.name = name; + return this; + } + + public String getDemoUrl() { + return demoUrl; + } + + public boolean hasDemoUrl() { + return !StringUtil.isEmpty(demoUrl); + } + + public TemplateResource setDemoUrl(String demoUrl) { + this.demoUrl = demoUrl; + return this; + } + + public int getPkgSize() { + return pkgSize; + } + + public TemplateResource setPkgSize(int pkgSize) { + this.pkgSize = pkgSize; + return this; + } + + public String getId() { + return id; + } + + public TemplateResource setId(String id) { + this.id = id; + return this; + } + + public String getUuid() { + return uuid; + } + + public TemplateResource setUuid(String uuid) { + this.uuid = uuid; + return this; + } +} \ No newline at end of file diff --git a/designer-realize/src/main/java/com/fr/design/mainframe/alphafine/model/TemplateResourceDetail.java b/designer-realize/src/main/java/com/fr/design/mainframe/alphafine/model/TemplateResourceDetail.java new file mode 100644 index 000000000..5065f6a2b --- /dev/null +++ b/designer-realize/src/main/java/com/fr/design/mainframe/alphafine/model/TemplateResourceDetail.java @@ -0,0 +1,239 @@ +package com.fr.design.mainframe.alphafine.model; + + +import com.fr.design.mainframe.alphafine.search.helper.FineMarketClientHelper; +import com.fr.design.mainframe.alphafine.search.manager.impl.TemplateResourceSearchManager; +import com.fr.json.JSONArray; +import com.fr.json.JSONObject; +import com.fr.log.FineLoggerFactory; +import com.fr.stable.StringUtils; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * 模板资源详细数据 + */ +public class TemplateResourceDetail { + + // 与对应的模板资源关联 + private final TemplateResource root; + private String info; + private String vendor; + private String htmlText; + private List detailInfo; + private String[] tagsId; + private List tagsName; + private double price; + private String parentPkgName = ""; + private String parentPkgUrl; + private String resourceUrl; + + public static final String ID = "id"; + public static final String INFO = "description"; + public static final String VENDOR = "vendor"; + public static final String DETAIL_INFO = "text"; + public static final String TAGS_ID = "cid"; + public static final String PRICE = "price"; + public static final String NAME = "name"; + public static final String PARENT_NAME = "parentName"; + public static final String PARENT_URL = "parentUrl"; + public static final String TAGS_NAME = "tagsName"; + public static final String URL = "url"; + + + + public TemplateResourceDetail(TemplateResource resource) { + this.root = resource; + } + + + public String getVendor() { + return vendor; + } + + public void setVendor(String vendor) { + this.vendor = vendor; + } + + public TemplateResource getRoot() { + return root; + } + + public String getInfo() { + return info; + } + + public void setInfo(String info) { + this.info = info; + } + + public List getDetailInfo() { + return detailInfo; + } + + public void setDetailInfo(List detailInfo) { + this.detailInfo = detailInfo; + } + + public String[] getTagsId() { + return tagsId; + } + + public void setTagsId(String[] tagsId) { + this.tagsId = tagsId; + } + + public List getTagsName() { + return tagsName; + } + + public String getTagsString() { + StringBuilder sb = new StringBuilder(); + if (tagsName != null) { + for (String tag : tagsName) { + sb.append(tag + " "); + } + } + return sb.toString(); + } + + public void setTagsName(List tagsName) { + this.tagsName = tagsName; + } + + public double getPrice() { + return price; + } + + public void setPrice(double price) { + this.price = price; + } + + public String getParentPkgName() { + return parentPkgName; + } + + public void setParentPkgName(String parentPkgName) { + if (StringUtils.isEmpty(parentPkgName)) { + this.parentPkgName = ""; + } else { + this.parentPkgName = parentPkgName; + } + } + + public String getResourceUrl() { + return resourceUrl; + } + + public void setResourceUrl(String resourceUrl) { + this.resourceUrl = resourceUrl; + } + + public String getHtmlText() { + return htmlText; + } + + public void setHtmlText(String htmlText) { + this.htmlText = htmlText; + } + + public String getParentPkgUrl() { + return parentPkgUrl; + } + + public void setParentPkgUrl(String parentPkgUrl) { + this.parentPkgUrl = parentPkgUrl; + } + + public static TemplateResourceDetail createByTemplateResource(TemplateResource root) { + return Builder.buildByResource(root); + } + + public static TemplateResourceDetail createFromEmbedResource(TemplateResource root) { + return Builder.buildFromEmbedResource(root); + } + + static class Builder { + + static FineMarketClientHelper helper = FineMarketClientHelper.getInstance(); + + static TemplateResourceDetail buildFromEmbedResource(TemplateResource templateResource) { + TemplateResourceDetail detail = new TemplateResourceDetail(templateResource); + String resourceId = templateResource.getId(); + JSONArray embedResources = TemplateResourceSearchManager.getInstance().getEmbedResourceJSONArray(); + for (int i = 0; i < embedResources.length(); i++) { + JSONObject resource = embedResources.getJSONObject(i); + if (resourceId.equals(resource.getString(ID))) { + detail.setInfo(resource.getString(INFO)); + detail.setHtmlText(resource.getString(DETAIL_INFO)); + detail.setVendor(resource.getString(VENDOR)); + detail.setPrice(resource.getDouble(PRICE)); + detail.setResourceUrl(resource.getString(URL)); + detail.setParentPkgName(resource.getString(PARENT_NAME)); + detail.setParentPkgUrl(resource.getString(PARENT_URL)); + detail.setTagsName(Arrays.asList(resource.getString(TAGS_NAME).split(","))); + break; + } + } + return detail; + } + + static TemplateResourceDetail buildByResource(TemplateResource templateResource) { + TemplateResourceDetail detail = new TemplateResourceDetail(templateResource); + String resourceId = templateResource.getId(); + + // 获取模板详情页的信息一共需要三次请求 + try { + // 1请求详细信息 + JSONObject info = helper.getTemplateInfoById(resourceId); + detail.setInfo(info.getString(INFO)); + detail.setHtmlText(info.getString(DETAIL_INFO)); + detail.setVendor(info.getString(VENDOR)); + detail.setTagsId(info.getString(TAGS_ID).split(",")); + detail.setPrice(info.getDouble(PRICE)); + detail.setResourceUrl(helper.getTemplateUrlById(templateResource.getId())); + + // 2请求所属模板包信息 + JSONObject parentPkginfo = helper.getTemplateParentPackageByTemplateId(resourceId); + if (parentPkginfo != null) { + detail.setParentPkgName(parentPkginfo.getString(NAME)); + detail.setParentPkgUrl(FineMarketClientHelper.getInstance().getTemplateUrlById(parentPkginfo.getString(ID))); + } + + // 3请求标签信息 + detail.setTagsName(helper.getTemplateTagsByTemplateTagIds(detail.getTagsId())); + return detail; + } catch (Exception e) { + FineLoggerFactory.getLogger().error(e, e.getMessage()); + } + + return null; + } + + /** + * 这里做下数据转换 + * 原始数据是html标签写的,如下 + * "

  1. 该模板需用10.0及以上版本设计器预览

  2. 该模板为库存场景解决方案的部分内容,全部内容可下载库存场景解决方案查看

  3. 为保障模板预览效果,建议安装新自适应插件(FR11.0版本插件已内置,无需手动安装),有使用需求或疑问,请联系帆软技术支持咨询

", + * + * 转换的后的数据 是原始数据中所有

标签内的(包括标签)的字符串(List),如上字符串会转为如下 + * List [

该模板需用10.0及以上版本设计器预览

, + *

该模板为库存场景解决方案的部分内容,全部内容可下载库存场景解决方案查看

, + *

为保障模板预览效果,建议安装新自适应插件(FR11.0版本插件已内置,无需手动安装),有使用需求或疑问,请联系帆软技术支持咨询

+ * ] + * */ + static final Pattern htmlPattern = Pattern.compile("

(.+?)

"); + static List parseDetailInfo(String htmlDetailInfo) { + List infos = new ArrayList<>(); + Matcher matcher = htmlPattern.matcher(htmlDetailInfo); + while (matcher.find()) { + infos.add(matcher.group()); + } + return infos; + } + } + +} \ No newline at end of file diff --git a/designer-realize/src/main/java/com/fr/design/mainframe/alphafine/preview/TemplateResourceDetailPane.java b/designer-realize/src/main/java/com/fr/design/mainframe/alphafine/preview/TemplateResourceDetailPane.java new file mode 100644 index 000000000..8d0ef703b --- /dev/null +++ b/designer-realize/src/main/java/com/fr/design/mainframe/alphafine/preview/TemplateResourceDetailPane.java @@ -0,0 +1,228 @@ +package com.fr.design.mainframe.alphafine.preview; + +import com.fr.design.constants.UIConstants; +import com.fr.design.dialog.link.MessageWithLink; +import com.fr.design.gui.icontainer.UIScrollPane; +import com.fr.design.i18n.Toolkit; +import com.fr.design.mainframe.alphafine.AlphaFineConstants; +import com.fr.design.mainframe.alphafine.action.StartUseAction; +import com.fr.design.mainframe.alphafine.component.TemplateResourceImagePanel; +import com.fr.design.mainframe.alphafine.model.TemplateResourceDetail; +import com.fr.design.utils.BrowseUtils; +import com.fr.design.utils.LinkStrUtils; +import com.fr.stable.StringUtils; + +import javax.swing.BorderFactory; +import javax.swing.JButton; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.SwingUtilities; +import java.awt.Color; +import java.awt.Dimension; +import java.awt.FlowLayout; +import java.awt.Graphics; +import java.awt.Graphics2D; +import java.awt.RenderingHints; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; + +public class TemplateResourceDetailPane extends JPanel { + + + private TemplateResourceDetail data; + + private TemplateResourceImagePanel imagePane; + private JPanel contentPane; + private UIScrollPane infoScrollPane; + private JPanel operatePane; + private UIScrollPane detailInfoPane; + + + private static final int IMAGE_HEIGHT = 170; + private static final int IMAGE_WIDTH = 310; + private static final int SCROLL_PANE_WIDTH = 315; + private static final int SCROLL_PANE_HEIGHT = 135; + private static final int CONTENT_PANE_WIDTH = 320; + private static final int CONTENT_PANE_HEIGHT = 180; + private static final int DETAIL_PANE_HEIGHT = 95; + private static final int TEXT_SCROLL_PANE_HEIGHT = 500; + private static final int PANE_WIDTH = 635; + private static final int BUTTON_WIDTH = 68; + private static final int BUTTON_HEIGHT = 20; + + private static final String GOTO_DETAIL = Toolkit.i18nText("Fine-Design_Report_AlphaFine_Template_Detail_GOTO_DETAIL"); + private static final String START_USE = Toolkit.i18nText("Fine-Design_Report_AlphaFine_Template_Detail_START_USE"); + private static final String VENDOR = Toolkit.i18nText("Fine-Design_Report_AlphaFine_Template_Detail_Vendor"); + private static final String TAGS = Toolkit.i18nText("Fine-Design_Report_AlphaFine_Template_Detail_Tags"); + private static final String PARENT_PACKAGE = Toolkit.i18nText("Fine-Design_Report_AlphaFine_Template_Detail_Parent_Package"); + private static final String DETAIL_INFO = Toolkit.i18nText("Fine-Design_Report_AlphaFine_Template_Detail_Info"); + private static final String FREE = Toolkit.i18nText("Fine-Design_Report_AlphaFine_Template_Detail_Price_Free"); + private static final String SPACE = " "; + private static final String LF = "
"; + private static final String RMB = "¥"; + + + private static final Color INFO_PANE_BACKGROUND = new Color(0xf9f9f9); + private static final Color INFO_PANE_FOREGROUND = new Color(0x5b5b5c); + private static final Color MORE_INFO_LINK = new Color(0x419bf9); + + private static final String HTML_FORMAT = "%s"; + private static final String DETAIL_INFO_HTML_FORMAT = "

" + DETAIL_INFO + "

%s"; + private static final String HTML_P_TAG_FORMAT = "

%s

"; + + + + public TemplateResourceDetailPane(TemplateResourceDetail detail) { + this.data = detail; + initComponent(); + this.setLayout(new FlowLayout(FlowLayout.LEFT)); + this.setBorder(BorderFactory.createEmptyBorder(10, 20, 0, 20)); + this.add(imagePane); + this.add(contentPane); + this.add(detailInfoPane); + this.setBackground(Color.WHITE); + SwingUtilities.invokeLater(()->{scrollToTop();}); + } + + /** + * scrollPane创建后会拉到最底下,初始化的时候手动拉到顶 + */ + public void scrollToTop() { + infoScrollPane.getVerticalScrollBar().setValue(0); + detailInfoPane.getVerticalScrollBar().setValue(0); + } + + private void initComponent() { + createImagePane(); + createContentPane(); + createDetailInfoScrollPane(); + } + + private void createContentPane() { + createInfoScrollPane(); + createOperatePane(); + contentPane = new JPanel(); + contentPane.setLayout(new FlowLayout(FlowLayout.LEFT)); + contentPane.setPreferredSize(new Dimension(CONTENT_PANE_WIDTH, CONTENT_PANE_HEIGHT)); + contentPane.add(infoScrollPane); + contentPane.add(operatePane); + contentPane.setBackground(Color.WHITE); + } + + /** + * 操作区:查看详情,立即使用 + */ + private void createOperatePane() { + operatePane = new JPanel(new FlowLayout(FlowLayout.LEFT)); + + JLabel emptyLabel = new JLabel(); + emptyLabel.setPreferredSize(new Dimension(145, 25)); + JLabel priceLabel = new JLabel(); + priceLabel.setForeground(Color.RED); + if (data.getPrice() == 0) { + priceLabel.setText(FREE); + } else { + priceLabel.setText(RMB + SPACE + data.getPrice()); + } + + operatePane.add(createLinkLabel()); + operatePane.add(emptyLabel); + operatePane.add(priceLabel); + operatePane.add(createStartUseButton()); + operatePane.setBackground(Color.WHITE); + + } + + /** + * 查看详情 + */ + JLabel createLinkLabel() { + JLabel linkLabel = new JLabel(GOTO_DETAIL); + linkLabel.setBackground(Color.WHITE); + linkLabel.setForeground(MORE_INFO_LINK); + linkLabel.addMouseListener(new MouseAdapter() { + @Override + public void mouseClicked(MouseEvent e) { + super.mouseClicked(e); + openResourceUrl(data.getResourceUrl()); + } + }); + return linkLabel; + } + + /** + * 方便埋点 + */ + void openResourceUrl(String url) { + BrowseUtils.browser(url); + } + + + /** + * “立即使用” 按钮 + */ + JButton createStartUseButton() { + JButton starUseButton = new JButton(START_USE) { + @Override + public void paintComponent(Graphics g) { + Graphics2D g2d = (Graphics2D) g; + g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); + g2d.setColor(UIConstants.FLESH_BLUE); + g2d.fillRoundRect(0, 0, getWidth(), getHeight(), 4, 4); + super.paintComponent(g2d); + } + }; + starUseButton.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0)); + starUseButton.setForeground(Color.WHITE); + starUseButton.setFont(AlphaFineConstants.MEDIUM_FONT); + starUseButton.addActionListener(new StartUseAction(data)); + starUseButton.setPreferredSize(new Dimension(BUTTON_WIDTH, BUTTON_HEIGHT)); + starUseButton.setContentAreaFilled(false); + return starUseButton; + } + + private void createImagePane() { + imagePane = new TemplateResourceImagePanel(data.getRoot(), IMAGE_WIDTH, IMAGE_HEIGHT); + } + + /** + * 基本信息页 + */ + private void createInfoScrollPane() { + + StringBuilder sb = new StringBuilder(); + + // 开发者 + sb.append(String.format(HTML_P_TAG_FORMAT, VENDOR + data.getVendor())); + // 标签 + sb.append(String.format(HTML_P_TAG_FORMAT, TAGS + data.getTagsString())); + // 所属模板包 + if (!StringUtils.isEmpty(data.getParentPkgName())) { + sb.append(String.format(HTML_P_TAG_FORMAT, PARENT_PACKAGE + LinkStrUtils.generateLinkTagWithoutUnderLine(data.getParentPkgUrl(), data.getParentPkgName()))); + } + // 信息 + sb.append(String.format(HTML_P_TAG_FORMAT, data.getInfo())); + + + MessageWithLink content = new MessageWithLink(String.format(HTML_FORMAT, sb)); + content.setBackground(INFO_PANE_BACKGROUND); + content.setForeground(INFO_PANE_FOREGROUND); + infoScrollPane = new UIScrollPane(content); + infoScrollPane.setPreferredSize(new Dimension(SCROLL_PANE_WIDTH, SCROLL_PANE_HEIGHT)); + infoScrollPane.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0)); + } + + /** + * 详细信息页 + */ + private void createDetailInfoScrollPane() { + MessageWithLink content = new MessageWithLink(String.format(DETAIL_INFO_HTML_FORMAT, data.getHtmlText())); + detailInfoPane = new UIScrollPane(content); + detailInfoPane.setPreferredSize(new Dimension(PANE_WIDTH, DETAIL_PANE_HEIGHT)); + detailInfoPane.setBackground(Color.WHITE); + detailInfoPane.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0)); + } + + + +} \ No newline at end of file diff --git a/designer-realize/src/main/java/com/fr/design/mainframe/alphafine/preview/TemplateShopPane.java b/designer-realize/src/main/java/com/fr/design/mainframe/alphafine/preview/TemplateShopPane.java new file mode 100644 index 000000000..fb0181ddd --- /dev/null +++ b/designer-realize/src/main/java/com/fr/design/mainframe/alphafine/preview/TemplateShopPane.java @@ -0,0 +1,183 @@ +package com.fr.design.mainframe.alphafine.preview; + +import com.fr.design.gui.ilable.UILabel; +import com.fr.design.mainframe.alphafine.AlphaFineConstants; +import com.fr.design.mainframe.alphafine.AlphaFineHelper; +import com.fr.design.mainframe.alphafine.component.TemplateResourcePageGridPane; +import com.fr.design.mainframe.alphafine.model.TemplateResource; +import com.fr.design.mainframe.alphafine.model.TemplateResourceDetail; +import com.fr.design.mainframe.alphafine.search.manager.impl.TemplateResourceSearchManager; +import com.fr.log.FineLoggerFactory; +import com.fr.third.apache.logging.log4j.util.Strings; + +import javax.swing.JPanel; +import javax.swing.SwingWorker; +import java.awt.CardLayout; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.util.List; + +public class TemplateShopPane extends JPanel { + + private static final TemplateShopPane INSTANCE = new TemplateShopPane(); + public static TemplateShopPane getInstance() { + return INSTANCE; + } + + // public 方便埋点 + public static final String DEFAULT_PAGE_PANEL = "defaultPagePane"; + public static final String PAGE_PANEL = "pagePane"; + public static final String DETAIL_PANEL = "detailPane"; + public static final String LOADING_PANEL = "loadingPane"; + public static final String FAILED_PANEL = "failedPane"; + private String currentCard = Strings.EMPTY; + private static final String SLASH = "/"; + + private CardLayout cardLayout = new CardLayout(); + private JPanel defaultPagePane; + private JPanel pagePane; + private JPanel detailPane; + private JPanel loadingPane; + private JPanel failedPane; + + private TemplateShopPane() { + setLayout(cardLayout); + initComponents(); + this.add(defaultPagePane, DEFAULT_PAGE_PANEL); + this.add(loadingPane, LOADING_PANEL); + this.setPreferredSize(AlphaFineConstants.PREVIEW_SIZE); + switchCard(DEFAULT_PAGE_PANEL); + } + + private void switchCard(String flag) { + cardLayout.show(this, flag); + currentCard = flag; + } + + private void initComponents() { + defaultPagePane = createDefaultResourcePane(); + loadingPane = createLoadingPane(); + } + + public void refreshPagePane(List resourceList) { + pagePane = createContentPane(resourceList); + this.add(pagePane, PAGE_PANEL); + switchCard(PAGE_PANEL); + } + + public void quitSearchResultPane() { + if (currentCard.equals(PAGE_PANEL)) { + switchCard(DEFAULT_PAGE_PANEL); + } + } + + public void showPagePane() { + switchCard(PAGE_PANEL); + } + + // 打开二级页面,显示详细信息 + public void searchAndShowDetailPane(TemplateResource resource) { + + changeLabel(resource.getName()); + + switchCard(LOADING_PANEL); + + new SwingWorker() { + @Override + protected TemplateResourceDetail doInBackground(){ + // 搜搜 + TemplateResourceDetail detail = TemplateResourceSearchManager.getInstance().getDetailSearchResult(resource); + return detail; + } + + @Override + protected void done() { + TemplateResourceDetail detail = null; + try { + detail = get(); + } catch (Exception e) { + FineLoggerFactory.getLogger().error(e, e.getMessage()); + } + + if (detail == null) { + setRetryAction(resource); + switchCard(FAILED_PANEL); + } else { + // detailpane初始化 + detailPane = new TemplateResourceDetailPane(detail); + // 切换 + INSTANCE.add(detailPane, DETAIL_PANEL); + switchCard(DETAIL_PANEL); + } + } + + + }.execute(); + + } + + + + private void changeLabel(String resourceName) { + JPanel labelNamePane = AlphaFineHelper.getAlphaFineDialog().getLabelWestPane(); + UILabel tabLabel = AlphaFineHelper.getAlphaFineDialog().getTabLabel(); + tabLabel.setForeground(AlphaFineConstants.DARK_GRAY); + + UILabel slash = new UILabel(SLASH); + slash.setForeground(AlphaFineConstants.DARK_GRAY); + + UILabel resourceLabel = new UILabel(resourceName); + resourceLabel.setForeground(AlphaFineConstants.LABEL_SELECTED); + + + labelNamePane.removeAll(); + labelNamePane.add(tabLabel); + labelNamePane.add(slash); + labelNamePane.add(resourceLabel); + + + tabLabel.addMouseListener(new MouseAdapter() { + @Override + public void mouseClicked(MouseEvent e) { + super.mouseClicked(e); + switchCard(PAGE_PANEL); + tabLabel.setForeground(AlphaFineConstants.LABEL_SELECTED); + labelNamePane.remove(slash); + labelNamePane.remove(resourceLabel); + } + }); + } + + // 方便埋点,勿删 + public String getCurrentCard() { + return currentCard; + } + + private JPanel createContentPane(List templateResources) { + return new TemplateResourcePageGridPane(templateResources); + } + + private JPanel createDefaultResourcePane() { + return createContentPane(TemplateResourceSearchManager.getInstance().getDefaultResourceList()); + } + + + private JPanel createLoadingPane() { + return new SearchLoadingPane(); + } + + private void setRetryAction(TemplateResource resource) { + if (failedPane != null) { + INSTANCE.remove(failedPane); + } + failedPane = createFailedPane(resource); + INSTANCE.add(failedPane, FAILED_PANEL); + } + + private JPanel createFailedPane(TemplateResource resource) { + return new NetWorkFailedPane(()->{this.searchAndShowDetailPane(resource);}); + } + + + +} \ No newline at end of file diff --git a/designer-realize/src/main/java/com/fr/design/mainframe/alphafine/search/TemplateResourceSearchWorkerManager.java b/designer-realize/src/main/java/com/fr/design/mainframe/alphafine/search/TemplateResourceSearchWorkerManager.java new file mode 100644 index 000000000..ac1ef39a7 --- /dev/null +++ b/designer-realize/src/main/java/com/fr/design/mainframe/alphafine/search/TemplateResourceSearchWorkerManager.java @@ -0,0 +1,109 @@ +package com.fr.design.mainframe.alphafine.search; + +import com.fr.design.mainframe.alphafine.AlphaFineConstants; +import com.fr.design.mainframe.alphafine.AlphaFineHelper; +import com.fr.design.mainframe.alphafine.CellType; +import com.fr.design.mainframe.alphafine.component.AlphaFineFrame; +import com.fr.design.mainframe.alphafine.model.TemplateResource; +import com.fr.design.mainframe.alphafine.preview.TemplateShopPane; +import com.fr.log.FineLoggerFactory; + +import javax.swing.SwingWorker; +import java.util.List; +import java.util.function.Function; + +public class TemplateResourceSearchWorkerManager implements SearchManager { + + private final CellType cellType; + + private SwingWorker, Void> searchWorker; + + private Function> searchFunction; + + private AlphaFineFrame alphaFineFrame; + + private volatile boolean searchResult = true; + + private volatile boolean searchOver = false; + + private volatile boolean networkError = false; + + public TemplateResourceSearchWorkerManager(CellType cellType, Function> searchFunction, AlphaFineFrame alphaFineFrame) { + this.cellType = cellType; + this.searchFunction = searchFunction; + this.alphaFineFrame = alphaFineFrame; + } + + @Override + public void doSearch(SearchTextBean searchTextBean) { + checkSearchWork(); + searchOver = false; + networkError = false; + + this.searchWorker = new SwingWorker, Void>() { + @Override + protected List doInBackground() { + List list; + if (!AlphaFineHelper.isNetworkOk() && cellType.isNeedNetWork()) { + networkError = true; + FineLoggerFactory.getLogger().warn("alphaFine network error"); + } + list = searchFunction.apply(searchTextBean); + return list; + } + + @Override + protected void done() { + searchOver = true; + if (!isCancelled()) { + try { + List list = get(); + searchResult = !list.isEmpty(); + showResult(list); + } catch (Exception e) { + FineLoggerFactory.getLogger().error(e.getMessage(), e); + } + } + } + }; + this.searchWorker.execute(); + } + + void showResult(List list) { + if (networkError && !searchResult) { + alphaFineFrame.showResult(AlphaFineConstants.NETWORK_ERROR); + return; + } + + if (alphaFineFrame.getSelectedType() == cellType) { + if (!searchResult) { + alphaFineFrame.showResult(CellType.NO_RESULT.getFlagStr4None()); + } else { + TemplateShopPane.getInstance().refreshPagePane(list); + AlphaFineHelper.getAlphaFineDialog().showResult(cellType.getFlagStr4None()); + } + } + } + + @Override + public boolean hasSearchResult() { + return searchResult; + } + + @Override + public boolean isSearchOver() { + return searchOver; + } + + private void checkSearchWork() { + if (this.searchWorker != null && !this.searchWorker.isDone()) { + this.searchWorker.cancel(true); + this.searchWorker = null; + } + } + + @Override + public boolean isNetWorkError() { + return networkError; + } +} \ No newline at end of file diff --git a/designer-realize/src/main/java/com/fr/design/mainframe/alphafine/search/helper/FineMarketClientHelper.java b/designer-realize/src/main/java/com/fr/design/mainframe/alphafine/search/helper/FineMarketClientHelper.java new file mode 100644 index 000000000..52e7e3a80 --- /dev/null +++ b/designer-realize/src/main/java/com/fr/design/mainframe/alphafine/search/helper/FineMarketClientHelper.java @@ -0,0 +1,325 @@ +package com.fr.design.mainframe.alphafine.search.helper; + +import com.fr.design.DesignerEnvManager; +import com.fr.design.extra.PluginConstants; +import com.fr.design.mainframe.alphafine.download.FineMarketDownloadManager; +import com.fr.design.mainframe.alphafine.model.TemplateResource; +import com.fr.file.FileCommonUtils; +import com.fr.general.CloudCenter; +import com.fr.general.http.HttpToolbox; +import com.fr.json.JSONArray; +import com.fr.json.JSONObject; +import com.fr.log.FineLoggerFactory; +import com.fr.market.key.FineMarketPublicKeyHolder; +import com.fr.security.SecurityToolbox; +import com.fr.stable.StableUtils; +import com.fr.third.org.apache.http.HttpEntity; +import com.fr.third.org.apache.http.HttpException; +import com.fr.third.org.apache.http.HttpStatus; +import com.fr.third.org.apache.http.client.config.CookieSpecs; +import com.fr.third.org.apache.http.client.config.RequestConfig; +import com.fr.third.org.apache.http.client.methods.CloseableHttpResponse; +import com.fr.third.org.apache.http.client.methods.HttpUriRequest; +import com.fr.third.org.apache.http.client.methods.RequestBuilder; +import com.fr.third.org.apache.http.impl.client.BasicCookieStore; +import com.fr.third.org.apache.http.impl.client.CloseableHttpClient; +import com.fr.third.org.apache.http.impl.client.HttpClients; +import org.jetbrains.annotations.Nullable; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class FineMarketClientHelper { + private static final FineMarketClientHelper INSTANCE = new FineMarketClientHelper(); + public static FineMarketClientHelper getInstance() { + return INSTANCE; + } + + private static final String CERTIFICATE_PUBLIC_KEY = FineMarketPublicKeyHolder.getInstance().getDefaultKey(); + public static final String FINE_MARKET_TEMPLATE_INFO = CloudCenter.getInstance().acquireUrlByKind("market.template.info"); + public static final String FINE_MARKET_TEMPLATE_URL = CloudCenter.getInstance().acquireUrlByKind("market.template.url"); + public static final String FILE_DOWNLOAD = "file/download/"; + public static final String PACKAGE_DOWNLOAD = "package/download/"; + public static final String TEMPLATES_PARENT_PACKAGE = "parent/"; + public static final String TEMPLATES_TAGS = "filter"; + public static final String NAME_SEARCH = "?searchKeyword="; + + public static final String RESPONSE_STATE = "state"; + public static final String RESPONSE_SUCCESS = "ok"; + public static final String RESPONSE_RESULT = "result"; + public static final String TAGS_KEY = "key"; + public static final String TAGS_ITEMS = "items"; + public static final String TAG_NAME = "name"; + public static final String TAG_ID = "id"; + + // 缓存下所有tag标签 + private Map tags; + + + /** + * 获取模板资源的下载链接 + * */ + public String getResourceDownloadUrl(TemplateResource templateResource) { + if (TemplateResource.Type.SCENARIO_SOLUTION.equals(templateResource.getType())) { + return getPackageDownloadUrl(); + } else { + return getFileDownLoadUrl(); + } + + } + + private String getPackageDownloadUrl() { + return FINE_MARKET_TEMPLATE_INFO + PACKAGE_DOWNLOAD; + } + + private String getFileDownLoadUrl() { + return FINE_MARKET_TEMPLATE_INFO + FILE_DOWNLOAD; + } + + + public String download(TemplateResource resource, File destDir, com.fr.design.extra.Process process) throws Exception { + String resourceId = resource.getId(); + + CloseableHttpResponse fileRes = getFileResponse(resource, resourceId); + + if (fileRes.getStatusLine().getStatusCode() == HttpStatus.SC_OK) { + + File destFile = createDestFile(destDir, resource); + + StableUtils.makesureFileExist(destFile); + + InputStream content = null; + FileOutputStream writer = null; + try { + writer = new FileOutputStream(FileCommonUtils.getAbsolutePath(destFile)); + + HttpEntity entity = fileRes.getEntity(); + long totalSize = entity.getContentLength(); + content = entity.getContent(); + + + byte[] data = new byte[PluginConstants.BYTES_NUM]; + int bytesRead; + int totalBytesRead = 0; + + while ((bytesRead = content.read(data)) > 0) { + writer.write(data, 0, bytesRead); + data = new byte[PluginConstants.BYTES_NUM]; + totalBytesRead += bytesRead; + process.process(totalBytesRead / (double) totalSize); + } + } catch (Exception e) { + FineLoggerFactory.getLogger().error(e, e.getMessage()); + } finally { + content.close(); + writer.flush(); + writer.close(); + } + + + FineLoggerFactory.getLogger().info("download resource{} success", resourceId); + process.process(FineMarketDownloadManager.PROCESS_SUCCESS); + + return FileCommonUtils.getAbsolutePath(destFile); + } else { + FineLoggerFactory.getLogger().info("download resource{} failed", resourceId); + process.process(FineMarketDownloadManager.PROCESS_FAILED); + throw new HttpException(); + } + + } + + + private CloseableHttpResponse getFileResponse(TemplateResource resource, String resourceId) throws Exception { + CloseableHttpResponse fileRes = postDownloadHttpResponse(getResourceDownloadUrl(resource), resourceId); + if (fileRes.getStatusLine().getStatusCode() == HttpStatus.SC_MOVED_TEMPORARILY) { + fileRes = getDownloadHttpResponse(fileRes.getHeaders("Location")[0].getValue()); + } + return fileRes; + } + + /** + * 在目标路径下(destDir)创建与资源名称相同的文件夹 (finalDir) + * 将资源下载到 destDir/finalDir + * 如果文件重复,则在文件名前加自增id + * */ + private File createDestFile(File destDir, TemplateResource resource) { + String fileName = resource.getName(); + try { + File finalDir = new File(StableUtils.pathJoin(FileCommonUtils.getAbsolutePath(destDir), fileName)); + if (!finalDir.exists()) { + finalDir.mkdir(); + } + + // 获取文件名(含后缀) + fileName = resource.getFileName(); + + // 处理重复文件名 + String fileNameFormat = "(%d)" + fileName; + Pattern pattern = Pattern.compile("\\((\\d)\\)" + fileName); + int cnt = 0; + + File[] files = finalDir.listFiles(); + for (File f : files) { + Matcher matcher = pattern.matcher(f.getName()); + if (matcher.find()) { + cnt = Math.max(cnt, Integer.parseInt(matcher.group(1))); + } + } + cnt++; + fileName = String.format(fileNameFormat, cnt); + + + + File destFile = new File(StableUtils.pathJoin(FileCommonUtils.getAbsolutePath(finalDir), fileName)); + destFile.createNewFile(); + return destFile; + } catch (Exception e) { + FineLoggerFactory.getLogger().error(e, e.getMessage()); + } + return null; + } + + private static CloseableHttpResponse getDownloadHttpResponse(String url) throws Exception { + //先登录一下。不然可能失败 + CloseableHttpClient client = createClient(); + HttpUriRequest file = RequestBuilder.get() + .setUri(url) + .build(); + return client.execute(file); + } + + private static CloseableHttpResponse postDownloadHttpResponse(String url, String id) throws Exception { + return postDownloadHttpResponse(url, id, new HashMap<>()); + } + + private static CloseableHttpResponse postDownloadHttpResponse(String url, String id, Map params) throws Exception { + //先登录一下。不然可能失败 + CloseableHttpClient client = createClient(); + FineLoggerFactory.getLogger().info("login fr-market"); + FineLoggerFactory.getLogger().info("start download resource {}", id); + RequestBuilder builder = RequestBuilder.post() + .setHeader("User-Agent", "Mozilla/5.0") + .setUri(url) + .addParameter("id", SecurityToolbox.encrypt(id, CERTIFICATE_PUBLIC_KEY)) + .addParameter("userId", String.valueOf(DesignerEnvManager.getEnvManager().getDesignerLoginUid())); + + if (params != null) { + Set keys = params.keySet(); + for (String key: keys) { + builder.addParameter(key, params.get(key)); + } + } + return client.execute(builder.build()); + } + + + private static CloseableHttpClient createClient() { + + BasicCookieStore cookieStore = new BasicCookieStore(); + return HttpClients.custom() + .setDefaultRequestConfig(RequestConfig.custom() + .setCookieSpec(CookieSpecs.STANDARD).build()) + .setDefaultCookieStore(cookieStore) + .build(); + } + + public @Nullable JSONObject getTemplateInfoById(String id) throws IOException { + String url = FINE_MARKET_TEMPLATE_INFO + id; + String jsonString = HttpToolbox.get(url); + JSONObject jsonObject = new JSONObject(jsonString); + String responseState = (String) jsonObject.get(RESPONSE_STATE); + if (RESPONSE_SUCCESS.equals(responseState)) { + return jsonObject.getJSONObject(RESPONSE_RESULT); + } else { + return null; + } + } + + public @Nullable JSONArray getTemplateInfoByName(String name) throws IOException { + String url = FINE_MARKET_TEMPLATE_INFO + NAME_SEARCH + name; + String jsonString = HttpToolbox.get(url); + JSONObject jsonObject = new JSONObject(jsonString); + String responseState = (String) jsonObject.get(RESPONSE_STATE); + if (RESPONSE_SUCCESS.equals(responseState)) { + return jsonObject.getJSONArray(RESPONSE_RESULT); + } + return null; + } + + public String getTemplateUrlById(String id) { + return FINE_MARKET_TEMPLATE_URL + id; + } + + public @Nullable JSONObject getTemplateParentPackageByTemplateId(String id) throws IOException { + String url = FINE_MARKET_TEMPLATE_INFO + TEMPLATES_PARENT_PACKAGE + id; + String jsonString = HttpToolbox.get(url); + JSONObject jsonObject = new JSONObject(jsonString); + String responseState = (String) jsonObject.get(RESPONSE_STATE); + if (RESPONSE_SUCCESS.equals(responseState)) { + JSONArray jsonArray = jsonObject.getJSONArray(RESPONSE_RESULT); + if (!jsonArray.isEmpty()) { + return jsonObject.getJSONArray(RESPONSE_RESULT).getJSONObject(0); + } + } + return null; + + } + + + /** + * 根据模板资源的tagid,获取tagName + * */ + public List getTemplateTagsByTemplateTagIds(String[] tagIds) throws IOException { + List list = new ArrayList<>(); + + initTags(); + + if (tagIds != null) { + for (String tagId : tagIds) { + String tagName = tags.get(tagId); + if (tagName != null) { + list.add(tagName); + } + } + } + + return list; + } + + /** + * 请求帆软市场,获取所有tag信息,并构建tagid - tagname的map + * */ + private void initTags() throws IOException { + tags = new HashMap<>(); + String url = FINE_MARKET_TEMPLATE_INFO + TEMPLATES_TAGS; + String jsonString = HttpToolbox.get(url); + JSONObject jsonObject = new JSONObject(jsonString); + String responseState = (String) jsonObject.get(RESPONSE_STATE); + if (RESPONSE_SUCCESS.equals(responseState)) { + JSONArray resultArray = jsonObject.getJSONArray(RESPONSE_RESULT); + for (int i = 1; i < resultArray.size(); i++) { + JSONObject result = resultArray.getJSONObject(i); + String key = result.getString(TAGS_KEY); + key = key.substring(key.indexOf('@') + 1); + JSONArray items = result.getJSONArray(TAGS_ITEMS); + for (int j = 0; j < items.length(); j++) { + JSONObject item = items.getJSONObject(j); + String id = item.getString(TAG_ID); + String name = item.getString(TAG_NAME); + tags.put(key + '-' + id, name); + } + } + } + } + +} \ No newline at end of file diff --git a/designer-realize/src/main/java/com/fr/design/mainframe/alphafine/search/manager/impl/ProductNewsSearchManager.java b/designer-realize/src/main/java/com/fr/design/mainframe/alphafine/search/manager/impl/ProductNewsSearchManager.java index 205735ab3..961121758 100644 --- a/designer-realize/src/main/java/com/fr/design/mainframe/alphafine/search/manager/impl/ProductNewsSearchManager.java +++ b/designer-realize/src/main/java/com/fr/design/mainframe/alphafine/search/manager/impl/ProductNewsSearchManager.java @@ -1,6 +1,7 @@ package com.fr.design.mainframe.alphafine.search.manager.impl; import com.fr.concurrent.NamedThreadFactory; +import com.fr.design.DesignerEnvManager; import com.fr.design.mainframe.alphafine.AlphaFineConstants; import com.fr.design.mainframe.alphafine.AlphaFineHelper; import com.fr.design.mainframe.alphafine.model.ProductNews; @@ -12,17 +13,18 @@ import com.fr.json.JSONObject; import com.fr.log.FineLoggerFactory; import org.jetbrains.annotations.Nullable; +import javax.imageio.ImageIO; import java.awt.Image; import java.net.URL; import java.util.ArrayList; import java.util.Date; +import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; -import javax.imageio.ImageIO; public class ProductNewsSearchManager { @@ -91,7 +93,7 @@ public class ProductNewsSearchManager { setImage(getCoverImage(obj.getString("pic"))). setUrl(obj.getString("url")).setTag(ProductNews.Tag.parseCode(obj.getInt("tag"))). setStatus(ProductNews.Status.parseCode(obj.getInt("status"))).setTarget( - ProductNews.Target.parseCode(obj.getInt("target"))). + ProductNews.ParseTarget(obj.getString("target"))). setCreator(obj.getInt("creator")).setPushDate(new Date(obj.getLong("push_time"))); Date currentDate = new Date(System.currentTimeMillis()); // 推送时间check @@ -100,9 +102,69 @@ public class ProductNewsSearchManager { idSet.add(productNews.getId()); } } + + productNewsList = filterByDesignerId(productNewsList); + return productNewsList; } + + /** + * 将productNews根据设计器id进行过滤 + * productNews有个target字段,代表推送对象用户组,检查设计器id是否在用户组中来进行过滤 + * */ + private List filterByDesignerId(List list) { + //设计器id + String designId = DesignerEnvManager.getEnvManager().getUUID(); + + HashMap> userGroupInfoCache = new HashMap<>(); + //遍历资源,获取target下的所有用户组信息,检查是否包含设计器id + List newsList = new ArrayList<>(); + for (ProductNews productNews : list) { + List targets = productNews.getTarget(); + + boolean targetsContainDesignerId = false; + + // 每条推送可能推送至多个用户组,需要逐一判断 + for (String userGroupId : targets) { + // 没有记录的用户组信息需要请求一下 + if (!userGroupInfoCache.containsKey(userGroupId)) { + userGroupInfoCache.put(userGroupId, searchUserGroupInfo(userGroupId)); + } + + // 判断设计器id是否在这个用户组中,在则退出判断,不在则继续 + if (userGroupInfoCache.get(userGroupId).contains(designId) || userGroupId.equals(ProductNews.ALL_USER_TARGET)) { + targetsContainDesignerId = true; + break; + } + } + + if (targetsContainDesignerId) { + newsList.add(productNews); + } + } + return newsList; + } + + /** + * 根据用户组id,查询用户组信息(改用户组中的所有设计器id) + * */ + private Set searchUserGroupInfo(String userGroupId) { + String url = AlphaFineConstants.ALPHA_CID_USER_GROUP_INFO + AlphaFineConstants.SEARCH_BY_ID + userGroupId; + Set idSet = new HashSet<>(); + try { + String jsonStr = HttpToolbox.get(url); + JSONObject jsonObject = new JSONObject(jsonStr); + JSONArray idArray = jsonObject.getJSONArray("data"); + for (int i = 0; i < idArray.length(); i++) { + idSet.add(idArray.getJSONObject(i).getString("userid")); + } + } catch (Exception e) { + FineLoggerFactory.getLogger().error(e, e.getMessage()); + } + return idSet; + } + public List getCachedProductNewsList() { return productNewsList; } diff --git a/designer-realize/src/main/java/com/fr/design/mainframe/alphafine/search/manager/impl/TemplateResourceSearchManager.java b/designer-realize/src/main/java/com/fr/design/mainframe/alphafine/search/manager/impl/TemplateResourceSearchManager.java new file mode 100644 index 000000000..2a8c76751 --- /dev/null +++ b/designer-realize/src/main/java/com/fr/design/mainframe/alphafine/search/manager/impl/TemplateResourceSearchManager.java @@ -0,0 +1,100 @@ +package com.fr.design.mainframe.alphafine.search.manager.impl; + + +import com.fr.design.mainframe.alphafine.AlphaFineHelper; +import com.fr.design.mainframe.alphafine.model.TemplateResource; +import com.fr.design.mainframe.alphafine.model.TemplateResourceDetail; +import com.fr.design.mainframe.alphafine.search.helper.FineMarketClientHelper; +import com.fr.general.CloudCenter; +import com.fr.general.IOUtils; +import com.fr.json.JSONArray; + +import java.util.ArrayList; +import java.util.List; + +public class TemplateResourceSearchManager { + + private static final TemplateResourceSearchManager INSTANCE = new TemplateResourceSearchManager(); + public static TemplateResourceSearchManager getInstance() { + return INSTANCE; + } + + public static final String LOCAL_RESOURCE_URL = "/com/fr/design/mainframe/alphafine/template_resource/local_templates.json"; + private static final FineMarketClientHelper helper = FineMarketClientHelper.getInstance(); + + + /** + * 帆软市场暂时没有分页搜索接口,先全量搜,分页展示 + * */ + public List getSearchResult(String searchText) { + List resourceList = new ArrayList<>(); + + // 联网搜索 + try { + JSONArray jsonArray = helper.getTemplateInfoByName(searchText); + if (jsonArray != null && !jsonArray.isEmpty()) { + resourceList.addAll(TemplateResource.createByJson(jsonArray)); + } + } catch (Exception e) { + + } + + // 本地搜索 + if (resourceList.isEmpty()) { + List localResource = getEmbedResourceList(); + localResource.stream().forEach(resource->{ + if (resource.getName().toLowerCase().contains(searchText)) { + resourceList.add(resource); + } + }); + } + return resourceList; + } + + + /** + * 返回默认资源 + * */ + public List getDefaultResourceList() { + List resourceList = getEmbedResourceList(); + // 添加推荐搜索卡片 + resourceList.add(TemplateResource.getRecommendSearch()); + return resourceList; + } + + /** + * 返回内置资源 + * */ + public List getEmbedResourceList() { + List resourceList = new ArrayList<>(); + JSONArray jsonArray = getEmbedResourceJSONArray(); + for (int i = 0; i < jsonArray.size(); i++) { + resourceList.add(TemplateResource.createByJson(jsonArray.getJSONObject(i))); + } + return resourceList; + } + + public JSONArray getEmbedResourceJSONArray() { + String jsonString = IOUtils.readResourceAsString(LOCAL_RESOURCE_URL); + return new JSONArray(jsonString); + } + + public List getRecommendSearchKeys() { + List searchKey = new ArrayList<>(); + String[] keys = CloudCenter.getInstance().acquireConf("alphafine.tempalte.recommend", "跑马灯,填报,地图").split(","); + for (String k : keys) { + searchKey.add(k); + } + return searchKey; + } + + + public TemplateResourceDetail getDetailSearchResult(TemplateResource resource) { + if (AlphaFineHelper.isNetworkOk()) { + return TemplateResourceDetail.createByTemplateResource(resource); + } else { + return TemplateResourceDetail.createFromEmbedResource(resource); + } + } + +} \ No newline at end of file diff --git a/designer-realize/src/main/java/com/fr/market/key/FineMarketDefaultKeyProperties.java b/designer-realize/src/main/java/com/fr/market/key/FineMarketDefaultKeyProperties.java new file mode 100644 index 000000000..d17a61727 --- /dev/null +++ b/designer-realize/src/main/java/com/fr/market/key/FineMarketDefaultKeyProperties.java @@ -0,0 +1,67 @@ +package com.fr.market.key; + +import com.fr.general.IOUtils; +import com.fr.io.utils.ResourceIOUtils; +import com.fr.log.FineLoggerFactory; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.HashMap; +import java.util.Map; +import java.util.Properties; + +/** + * @author Link + * @version 11.0 + * Created by Yvan on 2022/8/25 + */ +public class FineMarketDefaultKeyProperties { + + private Properties properties = new Properties(); + + private Map publicKeyMap = new HashMap<>(); + + private String propertyPath; + + private FineMarketDefaultKeyProperties(String propertyPath) { + this.propertyPath = propertyPath; + load(); + } + + public static FineMarketDefaultKeyProperties create(String propertyPath) { + return new FineMarketDefaultKeyProperties(propertyPath); + } + + private void load() { + try (InputStream inputStream = IOUtils.readResource(getPropertyPath())) { + byte[] data = ResourceIOUtils.inputStream2Bytes(inputStream); + properties.load(new ByteArrayInputStream(data)); + trims(properties); + publicKeyMap.put(FineMarketPublicKeyConstants.DEFAULT_KEY_KEY, properties.getProperty(FineMarketPublicKeyConstants.DEFAULT_KEY_KEY)); + } catch (IOException e) { + FineLoggerFactory.getLogger().error(e.getMessage(), e); + } + } + + private String getPropertyPath() { + return this.propertyPath; + } + + public String getPublicKey() { + return publicKeyMap.get(FineMarketPublicKeyConstants.DEFAULT_KEY_KEY); + } + + /** + * 去除properties中value末尾的空格 + * @param properties + */ + public static void trims(Properties properties) { + for (String key : properties.stringPropertyNames()) { + String value = properties.getProperty(key); + if (value != null) { + properties.put(key, value.trim()); + } + } + } +} diff --git a/designer-realize/src/main/java/com/fr/market/key/FineMarketPublicKeyConstants.java b/designer-realize/src/main/java/com/fr/market/key/FineMarketPublicKeyConstants.java new file mode 100644 index 000000000..cab87bec3 --- /dev/null +++ b/designer-realize/src/main/java/com/fr/market/key/FineMarketPublicKeyConstants.java @@ -0,0 +1,29 @@ +package com.fr.market.key; + + +/** + * @author Link + * @version 11.0 + * Created by Link on 2022/8/25 + */ +public class FineMarketPublicKeyConstants { + + public static final String DEFAULT_KEY_KEY = "defaultKey"; + + public static final String DEFAULT_KEY_DIRECTORY = "/com/fr/market/key"; + + /** + * 公钥第一段 + */ + public static final String FIRST_PROPERTY = "76c1/default"; + + /** + * 公钥第二段 + */ + public static final String SECOND_PROPERTY = "943f/default"; + + /** + * 公钥第三段 + */ + public static final String THIRD_PROPERTY = "d8a3/default"; +} diff --git a/designer-realize/src/main/java/com/fr/market/key/FineMarketPublicKeyHolder.java b/designer-realize/src/main/java/com/fr/market/key/FineMarketPublicKeyHolder.java new file mode 100644 index 000000000..b37493559 --- /dev/null +++ b/designer-realize/src/main/java/com/fr/market/key/FineMarketPublicKeyHolder.java @@ -0,0 +1,48 @@ +package com.fr.market.key; + +import com.fr.stable.StableUtils; + +/** + * 帆软市场公钥Holder + * @author Link + * @version 10.0 + * Created by Link on 2022/8/25 + */ +public class FineMarketPublicKeyHolder { + + private static FineMarketPublicKeyHolder instance = null; + + private String defaultKey; + + public static FineMarketPublicKeyHolder getInstance() { + + if (instance == null) { + synchronized (FineMarketPublicKeyHolder.class) { + if (instance == null) { + instance = new FineMarketPublicKeyHolder(); + } + } + } + return instance; + } + + private FineMarketPublicKeyHolder() { + init(); + } + + private void init() { + // 读取三个default.properties文件,组成公钥 + String firstPart = FineMarketDefaultKeyProperties.create(StableUtils.pathJoin(FineMarketPublicKeyConstants.DEFAULT_KEY_DIRECTORY, FineMarketPublicKeyConstants.FIRST_PROPERTY)).getPublicKey(); + String secondPart = FineMarketDefaultKeyProperties.create(StableUtils.pathJoin(FineMarketPublicKeyConstants.DEFAULT_KEY_DIRECTORY, FineMarketPublicKeyConstants.SECOND_PROPERTY)).getPublicKey(); + String thirdPart = FineMarketDefaultKeyProperties.create(StableUtils.pathJoin(FineMarketPublicKeyConstants.DEFAULT_KEY_DIRECTORY, FineMarketPublicKeyConstants.THIRD_PROPERTY)).getPublicKey(); + this.defaultKey = firstPart + secondPart + thirdPart; + } + + /** + * 获取默认公钥 + * @return 公钥 + */ + public String getDefaultKey() { + return this.defaultKey; + } +} diff --git a/designer-realize/src/main/java/com/fr/start/DesignerSubListener.java b/designer-realize/src/main/java/com/fr/start/DesignerSubListener.java index 52d364f6c..50134622d 100644 --- a/designer-realize/src/main/java/com/fr/start/DesignerSubListener.java +++ b/designer-realize/src/main/java/com/fr/start/DesignerSubListener.java @@ -8,6 +8,7 @@ import com.fr.exit.DesignerExiter; import com.fr.process.engine.core.CarryMessageEvent; import com.fr.process.engine.core.FineProcessContext; import com.fr.process.engine.core.FineProcessEngineEvent; +import com.fr.start.common.DesignerStartupContext; /** * @author hades @@ -31,6 +32,10 @@ public class DesignerSubListener { FineProcessContext.getParentPipe().listen(FineProcessEngineEvent.READY, new Listener() { @Override public void on(Event event, Null param) { + // 如果是在起始页的等待过程中 + if (DesignerStartupContext.getInstance().isOnWaiting()) { + return; + } if (DesignerContext.getDesignerFrame() == null || !DesignerContext.getDesignerFrame().isShowing()) { DesignerExiter.getInstance().exitUnexpectedly(() -> { diff --git a/designer-realize/src/main/java/com/fr/start/MainDesigner.java b/designer-realize/src/main/java/com/fr/start/MainDesigner.java index 9d4d3eb14..122f5f918 100644 --- a/designer-realize/src/main/java/com/fr/start/MainDesigner.java +++ b/designer-realize/src/main/java/com/fr/start/MainDesigner.java @@ -11,6 +11,7 @@ import com.fr.design.actions.server.ServerConfigManagerAction; import com.fr.design.actions.server.TemplateThemeManagerAction; import com.fr.design.actions.server.WidgetManagerAction; import com.fr.design.base.mode.DesignModeContext; +import com.fr.design.carton.SwitchForSwingChecker; import com.fr.design.constants.UIConstants; import com.fr.design.deeplink.DeepLinkManager; import com.fr.design.file.HistoryTemplateListCache; @@ -74,9 +75,8 @@ import com.fr.start.server.ServerTray; import com.fr.third.org.apache.commons.lang3.time.StopWatch; import com.fr.van.chart.map.server.ChartMapEditorAction; import com.fr.workspace.WorkContext; - -import javax.swing.JComponent; import javax.swing.JPanel; +import javax.swing.JComponent; import javax.swing.border.MatteBorder; import java.awt.Component; import java.awt.Dimension; @@ -123,12 +123,13 @@ public class MainDesigner extends BaseDesigner { showSplash(); startPreload0(); - DeepLinkManager.getInstance().start(args); StopWatch watch = new StopWatch(); watch.start(); DesignerLifecycleMonitorContext.getMonitor().beforeStart(); //启动运行时 FineRuntime.start(); + //等 FineRuntime 启动后启动 + DeepLinkManager.getInstance().start(args); startPreload1(); @@ -156,6 +157,8 @@ public class MainDesigner extends BaseDesigner { } FineLoggerFactory.getLogger().info("Designer started.Time used {} ms", watch.getTime()); watch.stop(); + + SwitchForSwingChecker.initThreadMonitoring(); } /** @@ -238,8 +241,13 @@ public class MainDesigner extends BaseDesigner { if (WorkContext.getCurrent().isRoot()) { menuDef.addShortCut( - new ServerConfigManagerAction(), - new TemplateThemeManagerAction(), + new ServerConfigManagerAction() + ); + JTemplate jt = HistoryTemplateListCache.getInstance().getCurrentEditingTemplate(); + if (JTemplate.isValid(jt)) { + menuDef.addShortCut(new TemplateThemeManagerAction()); + } + menuDef.addShortCut( new WidgetManagerAction() ); menuDef.addShortCut(new ChartPreStyleAction(), new ChartEmptyDataStyleAction(),new ChartMapEditorAction()); @@ -401,7 +409,7 @@ public class MainDesigner extends BaseDesigner { @Override protected void refreshLargeToolbarState() { JTemplate jt = HistoryTemplateListCache.getInstance().getCurrentEditingTemplate(); - if (jt == null) { + if (!JTemplate.isValid(jt)) { return; } saveButton.setEnabled(!jt.isSaved() && !DesignModeContext.isVcsMode() && jt.checkEnable()); diff --git a/designer-realize/src/main/java/com/fr/start/module/DesignerStartup.java b/designer-realize/src/main/java/com/fr/start/module/DesignerStartup.java index ff7962f61..4a4fbac2b 100644 --- a/designer-realize/src/main/java/com/fr/start/module/DesignerStartup.java +++ b/designer-realize/src/main/java/com/fr/start/module/DesignerStartup.java @@ -33,6 +33,7 @@ import com.fr.stable.StringUtils; import com.fr.stable.project.ProjectConstants; import com.fr.start.DesignerProcessType; import com.fr.start.ServerStarter; +import com.fr.start.common.DesignerStartupContext; import com.fr.start.event.LazyStartupEvent; import com.fr.start.preload.PreLoadService; import com.fr.start.server.FineEmbedServer; @@ -120,7 +121,7 @@ public class DesignerStartup extends Activator { || FineEmbedServer.isRunning()) { return; } - if (DaoSelectorFactory.getDaoSelector().useCacheDao()) { + if (DaoSelectorFactory.getDaoSelector().useCacheDao() || DesignerStartupContext.getInstance().isOnStartup()) { listenEvent(LazyStartupEvent.INSTANCE, new Listener(Integer.MIN_VALUE) { @Override public void on(Event event, Null param) { diff --git a/designer-realize/src/main/java/com/fr/start/module/optimized/DesignUpdateActivator.java b/designer-realize/src/main/java/com/fr/start/module/optimized/DesignUpdateActivator.java new file mode 100644 index 000000000..b6ad5ce2a --- /dev/null +++ b/designer-realize/src/main/java/com/fr/start/module/optimized/DesignUpdateActivator.java @@ -0,0 +1,25 @@ +package com.fr.start.module.optimized; + +import com.fr.start.common.DesignerStartupContext; +import com.fr.update.activator.BasicUpdateActivator; +import com.fr.update.base.FineUpdateUnit; + +/** + * created by Harrison on 2022/08/11 + **/ +public class DesignUpdateActivator extends BasicUpdateActivator { + + @Override + public void start() { + + if (DesignerStartupContext.getInstance().onWarmup()) { + try { + prepare4Start(); + FineUpdateUnit.makeNew(); + } catch (Throwable ignore) { + } + } else { + super.start(); + } + } +} diff --git a/designer-realize/src/main/java/com/fr/start/module/optimized/DesignerStartupPageActivator.java b/designer-realize/src/main/java/com/fr/start/module/optimized/DesignerStartupPageActivator.java index 12e1e1f7c..568b51293 100644 --- a/designer-realize/src/main/java/com/fr/start/module/optimized/DesignerStartupPageActivator.java +++ b/designer-realize/src/main/java/com/fr/start/module/optimized/DesignerStartupPageActivator.java @@ -1,5 +1,7 @@ package com.fr.start.module.optimized; +import com.fr.design.DesignerEnvManager; +import com.fr.design.mainframe.DesignerContext; import com.fr.design.ui.util.UIUtil; import com.fr.log.FineLoggerFactory; import com.fr.module.Activator; @@ -8,8 +10,11 @@ import com.fr.start.common.DesignerStartupContext; import com.fr.start.module.StartupArgs; import com.fr.start.util.DesignerStartupPageUtil; import com.fr.start.warmup.DesignerPreWarmTask; +import com.fr.startup.metric.DesignerMetrics; +import com.fr.startup.metric.DesignerStartupModel; import com.fr.startup.ui.StartupPageModel; import com.fr.startup.ui.StartupPageWindow; +import com.fr.startup.ui.StartupWorkspaceBean; import com.fr.third.org.apache.commons.lang3.time.StopWatch; import com.fr.value.NotNullLazyValue; import org.jetbrains.annotations.NotNull; @@ -56,8 +61,8 @@ public class DesignerStartupPageActivator extends Activator { warmTask.start(); // 即时暂停 - DesignerStartupContext.getRecorder().suspend(); - + suspendRecorder(context); + UIUtil.invokeLaterIfNeeded(() -> { StartupPageModel model = StartupPageModel.create(); @@ -66,24 +71,48 @@ public class DesignerStartupPageActivator extends Activator { // selectAndOpenLast model.setOpenLastTemplateRunnable(() -> { context.setOpenLastFile(true); + handleModel(model); launchAfterWarmup(warmTask); }); // selectAndOpenEmpty model.setOpenEmptyTemplateRunnable(() -> { context.setOpenEmpty(true); + handleModel(model); launchAfterWarmup(warmTask); }); // selectAndCreateNew model.setCreateNewTemplateRunnable(() -> { context.setCreateNew(true); + handleModel(model); launchAfterWarmup(warmTask); }); StartupPageWindow window = new StartupPageWindow(model); window.setVisible(true); context.setOnWaiting(true); + + }); + } + + private void suspendRecorder(DesignerStartupContext context) { + + DesignerMetrics designerMetrics = context.getDesignerMetrics(); + DesignerStartupModel designerStartupModel = designerMetrics.getModel(); + StopWatch recorder = DesignerStartupContext.getRecorder(); + recorder.suspend(); + long time = recorder.getTime(TimeUnit.MILLISECONDS); + designerStartupModel.setLandingTime(time); + } + + private void handleModel(StartupPageModel model) { + + // 将选中的环境设置为当前环境 + StartupWorkspaceBean selectWorkspaceInfo = model.getSelectWorkspaceInfo(); + DesignerEnvManager.getEnvManager().setCurEnvName(selectWorkspaceInfo.getName()); + UIUtil.invokeLaterIfNeeded(() -> { + DesignerContext.getDesignerFrame().setTitle(); }); } @@ -106,12 +135,22 @@ public class DesignerStartupPageActivator extends Activator { UIUtil.invokeLaterIfNeeded(() -> { // 换到 awt 线程中关闭,不然异步会出现问题。 DesignerStartupContext.getInstance().setOnStartup(false); + recordStartupEnd(stopWatch); }); } - + FineLoggerFactory.getLogger().debug("designer-startup-page started cost {} ms", DesignerStartupContext.getRecorder().getTime(TimeUnit.MILLISECONDS)); } + private void recordStartupEnd(StopWatch stopWatch) { + + DesignerStartupContext context = DesignerStartupContext.getInstance(); + DesignerMetrics designerMetrics = context.getDesignerMetrics(); + DesignerStartupModel model = designerMetrics.getModel(); + model.setStartingTime(stopWatch.getTime(TimeUnit.MILLISECONDS)); + model.fill(); + } + @Override public void stop() { diff --git a/designer-realize/src/main/resources/com/fr/design/mainframe/alphafine/images/bottom.svg b/designer-realize/src/main/resources/com/fr/design/mainframe/alphafine/images/bottom.svg new file mode 100644 index 000000000..ef489cdce --- /dev/null +++ b/designer-realize/src/main/resources/com/fr/design/mainframe/alphafine/images/bottom.svg @@ -0,0 +1,3 @@ + + + diff --git a/designer-realize/src/main/resources/com/fr/design/mainframe/alphafine/images/bottom_disable.svg b/designer-realize/src/main/resources/com/fr/design/mainframe/alphafine/images/bottom_disable.svg new file mode 100644 index 000000000..e924fcb81 --- /dev/null +++ b/designer-realize/src/main/resources/com/fr/design/mainframe/alphafine/images/bottom_disable.svg @@ -0,0 +1,3 @@ + + + diff --git a/designer-realize/src/main/resources/com/fr/design/mainframe/alphafine/images/caution.svg b/designer-realize/src/main/resources/com/fr/design/mainframe/alphafine/images/caution.svg new file mode 100644 index 000000000..731e79d0d --- /dev/null +++ b/designer-realize/src/main/resources/com/fr/design/mainframe/alphafine/images/caution.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/designer-realize/src/main/resources/com/fr/design/mainframe/alphafine/images/config.svg b/designer-realize/src/main/resources/com/fr/design/mainframe/alphafine/images/config.svg new file mode 100644 index 000000000..f322cde88 --- /dev/null +++ b/designer-realize/src/main/resources/com/fr/design/mainframe/alphafine/images/config.svg @@ -0,0 +1,3 @@ + + + diff --git a/designer-realize/src/main/resources/com/fr/design/mainframe/alphafine/images/down.svg b/designer-realize/src/main/resources/com/fr/design/mainframe/alphafine/images/down.svg new file mode 100644 index 000000000..9bc79b1c0 --- /dev/null +++ b/designer-realize/src/main/resources/com/fr/design/mainframe/alphafine/images/down.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/designer-realize/src/main/resources/com/fr/design/mainframe/alphafine/images/down_disable.svg b/designer-realize/src/main/resources/com/fr/design/mainframe/alphafine/images/down_disable.svg new file mode 100644 index 000000000..ec61156a0 --- /dev/null +++ b/designer-realize/src/main/resources/com/fr/design/mainframe/alphafine/images/down_disable.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/designer-realize/src/main/resources/com/fr/design/mainframe/alphafine/images/loading.svg b/designer-realize/src/main/resources/com/fr/design/mainframe/alphafine/images/loading.svg new file mode 100644 index 000000000..1df6ef5f2 --- /dev/null +++ b/designer-realize/src/main/resources/com/fr/design/mainframe/alphafine/images/loading.svg @@ -0,0 +1,3 @@ + + + diff --git a/designer-realize/src/main/resources/com/fr/design/mainframe/alphafine/images/local_template1.png b/designer-realize/src/main/resources/com/fr/design/mainframe/alphafine/images/local_template1.png new file mode 100644 index 000000000..203701cba Binary files /dev/null and b/designer-realize/src/main/resources/com/fr/design/mainframe/alphafine/images/local_template1.png differ diff --git a/designer-realize/src/main/resources/com/fr/design/mainframe/alphafine/images/local_template2.png b/designer-realize/src/main/resources/com/fr/design/mainframe/alphafine/images/local_template2.png new file mode 100644 index 000000000..a16f0dc40 Binary files /dev/null and b/designer-realize/src/main/resources/com/fr/design/mainframe/alphafine/images/local_template2.png differ diff --git a/designer-realize/src/main/resources/com/fr/design/mainframe/alphafine/images/local_template3.png b/designer-realize/src/main/resources/com/fr/design/mainframe/alphafine/images/local_template3.png new file mode 100644 index 000000000..89fd1386d Binary files /dev/null and b/designer-realize/src/main/resources/com/fr/design/mainframe/alphafine/images/local_template3.png differ diff --git a/designer-realize/src/main/resources/com/fr/design/mainframe/alphafine/images/local_template4.png b/designer-realize/src/main/resources/com/fr/design/mainframe/alphafine/images/local_template4.png new file mode 100644 index 000000000..fd30a4b7b Binary files /dev/null and b/designer-realize/src/main/resources/com/fr/design/mainframe/alphafine/images/local_template4.png differ diff --git a/designer-realize/src/main/resources/com/fr/design/mainframe/alphafine/images/more.png b/designer-realize/src/main/resources/com/fr/design/mainframe/alphafine/images/more.png new file mode 100644 index 000000000..e49b1c96f Binary files /dev/null and b/designer-realize/src/main/resources/com/fr/design/mainframe/alphafine/images/more.png differ diff --git a/designer-realize/src/main/resources/com/fr/design/mainframe/alphafine/images/next.svg b/designer-realize/src/main/resources/com/fr/design/mainframe/alphafine/images/next.svg new file mode 100644 index 000000000..ba63a3d99 --- /dev/null +++ b/designer-realize/src/main/resources/com/fr/design/mainframe/alphafine/images/next.svg @@ -0,0 +1,3 @@ + + + diff --git a/designer-realize/src/main/resources/com/fr/design/mainframe/alphafine/images/prev.svg b/designer-realize/src/main/resources/com/fr/design/mainframe/alphafine/images/prev.svg new file mode 100644 index 000000000..b70a6199a --- /dev/null +++ b/designer-realize/src/main/resources/com/fr/design/mainframe/alphafine/images/prev.svg @@ -0,0 +1,3 @@ + + + diff --git a/designer-realize/src/main/resources/com/fr/design/mainframe/alphafine/images/top.svg b/designer-realize/src/main/resources/com/fr/design/mainframe/alphafine/images/top.svg new file mode 100644 index 000000000..b82ae8475 --- /dev/null +++ b/designer-realize/src/main/resources/com/fr/design/mainframe/alphafine/images/top.svg @@ -0,0 +1,3 @@ + + + diff --git a/designer-realize/src/main/resources/com/fr/design/mainframe/alphafine/images/top_disable.svg b/designer-realize/src/main/resources/com/fr/design/mainframe/alphafine/images/top_disable.svg new file mode 100644 index 000000000..b507888ef --- /dev/null +++ b/designer-realize/src/main/resources/com/fr/design/mainframe/alphafine/images/top_disable.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/designer-realize/src/main/resources/com/fr/design/mainframe/alphafine/images/up.svg b/designer-realize/src/main/resources/com/fr/design/mainframe/alphafine/images/up.svg new file mode 100644 index 000000000..625153a57 --- /dev/null +++ b/designer-realize/src/main/resources/com/fr/design/mainframe/alphafine/images/up.svg @@ -0,0 +1,3 @@ + + + diff --git a/designer-realize/src/main/resources/com/fr/design/mainframe/alphafine/images/up_disable.svg b/designer-realize/src/main/resources/com/fr/design/mainframe/alphafine/images/up_disable.svg new file mode 100644 index 000000000..d84b3b3c5 --- /dev/null +++ b/designer-realize/src/main/resources/com/fr/design/mainframe/alphafine/images/up_disable.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/designer-realize/src/main/resources/com/fr/design/mainframe/alphafine/template_resource/local_templates.json b/designer-realize/src/main/resources/com/fr/design/mainframe/alphafine/template_resource/local_templates.json new file mode 100644 index 000000000..4e9265b14 --- /dev/null +++ b/designer-realize/src/main/resources/com/fr/design/mainframe/alphafine/template_resource/local_templates.json @@ -0,0 +1,110 @@ +[ + { + "id": 20000770, + "name": "PDCA销售运营闭环管理方案", + "uuid": "7975ad73-9aea-4634-96f1-d33ec1b4283c", + "vendor": "Victoria.", + "sellerId": 202, + "cid": "industry-3,purpose-2,purpose-16,purpose-15", + "tag": null, + "pic": "com/fr/design/mainframe/alphafine/images/local_template1.png", + "price": 0, + "fileLoca": null, + "text": "
  1. 此方案共包括14张模板,其中12张可视化分析看板、1张分析报表以及1张填报表,需在10.0及以上版本设计器中预览;

  2. 了解方案详情,可移步-数据分析与可视化指南

  3. 如果您希望在线体验当前方案,可移步-官方DEMO系统:业务场景应用->营销管理

  4. 如果您对此方案感兴趣,希望更加深入了解,可以填写此表单:方案需求录入;后续会有帆软行业顾问将与您联系。

", + "description": "将PDCA循环嵌入销售运营管理的全流程中,从决策层、管理层与执行层三个层级,对目标制定、执行过程、复盘分析、问题跟踪进行全方位精细化管理。", + "updateTime": "2022-06-15T06:36:39.000Z", + "downloadTimes": 1152, + "state": 1, + "designVersion": "10.0", + "involvePlugins": null, + "uploadTime": "2022-06-15T06:36:39.000Z", + "style": null, + "link": "template", + "sellerName": "Victoria.", + "pluginName": [], + "pkgsize": 14, + "material": "方案附件.rar", + "tagsName": "制造加工,营销,组织,管理,经营汇报" + }, + { + "id": 20000733, + "name": "库存场景解决方案", + "uuid": "43d1c14b-1a73-41e6-adcc-aaf2872bc8d4", + "vendor": "Victoria.", + "sellerId": 202, + "cid": "industry-3,purpose-5", + "tag": null, + "pic": "com/fr/design/mainframe/alphafine/images/local_template2.png", + "price": 0, + "fileLoca": null, + "text": "

当前库存管理面临的现状:库存成本劣势明显、库存周转差距较大。如果对此没有合理有效的解决方案,往往会导致企业库存越来越混乱,经营、资金、成本等问题丛生。

库存管理解决方案从:“盘”、“析”、“管”三个方向开展,彻底解决库存量大、结构复杂、管理混乱等常见问题

", + "description": "库存管理解决方案即从:“盘”、“析”、“管”三个方向开展,对导致库存管理问题的原因逐一击破。", + "updateTime": "2022-05-05T03:53:55.000Z", + "downloadTimes": 816, + "state": 1, + "designVersion": "10.0", + "involvePlugins": null, + "uploadTime": "2022-05-05T03:53:55.000Z", + "style": null, + "link": "template", + "sellerName": "Victoria.", + "pluginName": [], + "pkgsize": 11, + "material": "", + "tagsName":"制造加工,库存" + }, + { + "id": 20000581, + "name": "采购场景解决方案", + "uuid": "7994b01f-5069-4554-83cf-9d3506e30767", + "vendor": "Victoria.", + "sellerId": 202, + "cid": "industry-3,purpose-3", + "tag": null, + "pic": "com/fr/design/mainframe/alphafine/images/local_template3.png", + "price": 0, + "fileLoca": null, + "text": "

采购场景方案具体细节可参考:可视化指南-采购场景应用

", + "description": "采购解决方案采用:“自上而下”的分析思路,针对采购相关的不同角色层级及其不同角度发数据分析需求,产出不同的内容", + "updateTime": "2022-03-10T03:50:17.000Z", + "downloadTimes": 2353, + "state": 1, + "designVersion": "10.0", + "involvePlugins": null, + "uploadTime": "2022-03-10T03:50:18.000Z", + "style": null, + "link": "template", + "sellerName": "Victoria.", + "pluginName": [], + "pkgsize": 6, + "material": "", + "tagsName": "制造加工,采购" + }, + { + "id": 20000747, + "name": "费用预算系统解决方案", + "uuid": "0776533469c3401a8da78706856b6b02", + "vendor": "finereport", + "sellerId": 1, + "cid": "template_type-1,terminal-1,industry-13,purpose-1", + "tag": null, + "pic": "com/fr/design/mainframe/alphafine/images/local_template4.png", + "price": 0, + "fileLoca": "费用预算系统解决方案.zip", + "text": "

模板中使用了数据查询数据集,如果要在本地打开,需要在自己的数据库中建表,并修改数据连接。

1)建表语句见附件文件夹:SQL语句与表。


2)数据连接默认为 FRDemo,如果用户表所在的数据库连接不是 FRDemo,将模板中数据连接修改为用户默认的数据连接。


3)模板中 SQL 语句为 MYSQL 数据库,若用户使用中数据库语句不匹配,可对应修改为匹配的语句。


4)模板中有大量的超链接,用户存在本地后,预览时注意修改超链接。


5)费用预算系统的平台沿用人事管理系统的平台配置,如果要在本地体验效果,可先下载人事管理系统部署:人事管理系统

模板中图标等背景样式见背景附件。 如果数据替换麻烦,可直接使用内置数据集模板,但内置模板只提供了样式,控件、数据等不能联动。如何制作,可查看帮助文档:费用预算系统

", + "description": "费用预算系统是通过 FineReport 填报功能和平台功能结合实现的审批流程系统。能够在线采集数据,提供标准数据模板,解决不同部门、子公司之间数据不统一的问题,\n实现记录预算审批与更改的操作,审批与更改记录留痕等。\n详细内容可查看帮助文档:费用预算系统", + "updateTime": "2022-05-15T05:34:45.000Z", + "downloadTimes": 141, + "state": 1, + "designVersion": "10.0", + "involvePlugins": null, + "uploadTime": "2022-05-15T05:34:45.000Z", + "style": "简约清新", + "link": "template", + "sellerName": "finereport", + "pluginName": [], + "pkgsize": 1, + "material": null, + "tagsName": "IT互联网,财务" + } +] \ No newline at end of file diff --git a/designer-realize/src/main/resources/com/fr/market/key/76c1/default b/designer-realize/src/main/resources/com/fr/market/key/76c1/default new file mode 100644 index 000000000..58044d9c0 --- /dev/null +++ b/designer-realize/src/main/resources/com/fr/market/key/76c1/default @@ -0,0 +1 @@ +defaultKey=MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCtsz62CPSWXZE/IYZRiAuTSZkw \ No newline at end of file diff --git a/designer-realize/src/main/resources/com/fr/market/key/943f/default b/designer-realize/src/main/resources/com/fr/market/key/943f/default new file mode 100644 index 000000000..80946f51c --- /dev/null +++ b/designer-realize/src/main/resources/com/fr/market/key/943f/default @@ -0,0 +1 @@ +defaultKey=1WOwer8+JFktK0uKLAUuQoBr+UjAMFtRA8W7JgKMDwZy/2liEAiXEOSPU/hrdV8D \ No newline at end of file diff --git a/designer-realize/src/main/resources/com/fr/market/key/d8a3/default b/designer-realize/src/main/resources/com/fr/market/key/d8a3/default new file mode 100644 index 000000000..af41473f3 --- /dev/null +++ b/designer-realize/src/main/resources/com/fr/market/key/d8a3/default @@ -0,0 +1 @@ +defaultKey=tT541LnGi1X/hXiRwuttPWYN3L2GYm/d5blU+FBNwghBIrdAxXTzYBc6P4KL/oYXnMdTIrkz8tYkG3QoFQIDAQAB \ No newline at end of file