diff --git a/build.gradle b/build.gradle index 617b9d0fa..8399410db 100644 --- a/build.gradle +++ b/build.gradle @@ -73,6 +73,7 @@ allprojects { implementation 'com.fr.decision:fine-decision:' + frVersion implementation 'com.fr.schedule:fine-schedule:' + frVersion implementation 'com.fr.report:engine-report:' + frDevVersion + implementation 'com.fr.report:engine-x:' + frDevVersion implementation 'com.fr.report:engine-chart:' + frDevVersion implementation 'com.fr.report:engine-i18n:' + frDevVersion implementation 'com.fr.design:design-i18n:' + frDevVersion diff --git a/designer-base/src/main/java/com/fr/design/DesignModelAdapter.java b/designer-base/src/main/java/com/fr/design/DesignModelAdapter.java index e5930ee91..24dd31484 100644 --- a/designer-base/src/main/java/com/fr/design/DesignModelAdapter.java +++ b/designer-base/src/main/java/com/fr/design/DesignModelAdapter.java @@ -1,11 +1,8 @@ package com.fr.design; import com.fr.base.Parameter; -import com.fr.base.ParameterConfig; -import com.fr.base.TableData; import com.fr.base.io.BaseBook; import com.fr.data.TableDataSource; -import com.fr.data.operator.DataOperator; import com.fr.design.file.HistoryTemplateListCache; import com.fr.design.mainframe.DesignerFrameFileDealerPane; import com.fr.design.mainframe.JTemplate; @@ -14,14 +11,9 @@ import com.fr.form.ui.DataControl; import com.fr.form.ui.MultiFileEditor; import com.fr.form.ui.Widget; import com.fr.general.ComparatorUtils; -import com.fr.log.FineLoggerFactory; -import com.fr.stable.Filter; -import com.fr.stable.ParameterProvider; import com.fr.stable.StringUtils; import com.fr.stable.js.WidgetName; -import com.fr.util.ParameterApplyHelper; -import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; @@ -48,7 +40,7 @@ public abstract class DesignModelAdapter tableDataParametersMap = new HashMap<>(); - - public DesignModelAdapter(S jTemplate) { this.jTemplate = jTemplate; @@ -255,79 +241,6 @@ public abstract class DesignModelAdapter map, Filter filter) { - Iterator it = this.getBook().getTableDataNameIterator(); - try { - // 清空下缓存 - tableDataParametersMap.clear(); - while (it.hasNext()) { - String name = it.next(); - TableData tableData = this.getBook().getTableData(name); - ParameterProvider[] parameterProviders = DataOperator.getInstance().getTableDataParameters(tableData); - if (filter != null) { - ParameterApplyHelper.addPara2Map(map, parameterProviders, filter); - } else { - ParameterApplyHelper.addPara2Map(map, parameterProviders); - } - tableDataParametersMap.put(name, parameterProviders); - } - } catch (Exception e) { - FineLoggerFactory.getLogger().error(e.getMessage(), e); - } - } - - protected void updateTableDataParameters(String oldName, - String tdName, - TableData tableData, - Map map, - Filter filter) { - - ParameterProvider[] providers = null; - - if (!ComparatorUtils.equals(oldName, tdName)) { - // 更新下缓存名称 替换下数据集名称 - providers = tableDataParametersMap.remove(oldName); - if (providers != null) { - tableDataParametersMap.put(tdName, providers); - } - } - - - try { - for (Map.Entry entry : tableDataParametersMap.entrySet()) { - ParameterProvider[] parameterProviders; - if (ComparatorUtils.equals(entry.getKey(), tdName)) { - parameterProviders = DataOperator.getInstance().getTableDataParameters(tableData); - // 用来更新缓存 - providers = parameterProviders; - } else { - parameterProviders = entry.getValue(); - } - - if (filter != null) { - ParameterApplyHelper.addPara2Map(map, parameterProviders, filter); - } else { - ParameterApplyHelper.addPara2Map(map, parameterProviders); - } - - } - } catch (Exception e) { - FineLoggerFactory.getLogger().error(e.getMessage(), e); - } - - if (providers != null) { - // 更新缓存 - tableDataParametersMap.put(tdName, providers); - } - } - - protected void addGlobalParameters(Map map) { - // 添加全局参数 - Parameter[] glbParas = ParameterConfig.getInstance().getGlobalParameters(); - ParameterApplyHelper.addPara2Map(map, glbParas); - - } - /** * 环境改变. */ @@ -338,11 +251,6 @@ public abstract class DesignModelAdapter jTemplate = HistoryTemplateListCache.getInstance().getCurrentEditingTemplate(); + if (jTemplate != null) { + IOFile ioFile = (IOFile) jTemplate.getTarget(); + + attr = ioFile.getAttrMark(StrategyConfigsAttr.ATTR_MARK); + if (attr == null) { + attr = new StrategyConfigsAttr(); + ioFile.addAttrMark(attr); + } + + //新建模版此时不存在,不需要注册钩子 + if (attr.getXmlSavedHook() == null && WorkContext.getWorkResource().exist(jTemplate.getPath())) { + attr.setXmlSavedHook(new StrategyConfigsAttrSavedHook(jTemplate.getPath(), attr)); + } + return attr; + } else { + throw new IllegalStateException("[ESD]No editing template found."); + } + } + + + /** + * 当前编辑的模版是否被批量开启 + * + * @return + */ + public static boolean isEditingTemplateRecommended() { + JTemplate jTemplate = HistoryTemplateListCache.getInstance().getCurrentEditingTemplate(); + + if (jTemplate != null) { + FILE file = jTemplate.getEditingFILE(); + if (file != null) { + String path = file.getPath(); + return StrategyConfigHelper.recommended(path); + } + } + return false; + } + + + /** + * 获取模版数据集配置 + * + * @param dsName + * @return + */ + public static StrategyConfig getStrategyConfig(String dsName) { + + return getStrategyConfigsAttr().getStrategyConfig(dsName); + } + + /** + * 移除模版数据集配置 + * + * @param dsName + * @return + */ + public static StrategyConfig removeStrategyConfig(String dsName) { + + return getStrategyConfigsAttr().removeStrategyConfig(dsName); + } + + /** + * 添加模版数据集配置 + * + * @param strategyConfig + */ + public static void addStrategyConfig(StrategyConfig strategyConfig) { + + getStrategyConfigsAttr().addStrategyConfig(strategyConfig); + } + + + private static class StrategyConfigsAttrSavedHook implements XMLSavedHook { + + private static final long serialVersionUID = -8843201977112289321L; + + private final String tplPath; + private final Map origStrategyConfigs; + + public StrategyConfigsAttrSavedHook(String tplPath, StrategyConfigsAttr raw) { + this.tplPath = tplPath; + this.origStrategyConfigs = new HashMap<>(); + this.initOrigStrategyConfigs(raw); + } + + + private void initOrigStrategyConfigs(StrategyConfigsAttr raw) { + origStrategyConfigs.clear(); + raw.getStrategyConfigs().forEach((k, v) -> { + try { + origStrategyConfigs.put(k, v.clone()); + } catch (CloneNotSupportedException e) { + FineLoggerFactory.getLogger().error(e.getMessage(), e); + } + }); + } + + @Override + public void doAfterSaved(StrategyConfigsAttr saved) { + + FineLoggerFactory.getLogger().info("[ESD]Write StrategyConfigsAttr done, now check change."); + Set dsNames = new HashSet<>(); + dsNames.addAll(origStrategyConfigs.keySet()); + dsNames.addAll(saved.getStrategyConfigs().keySet()); + + if (StringUtils.isNotEmpty(tplPath)) { + dsNames.forEach(dsName -> { + if (StringUtils.isNotEmpty(dsName)) { + StrategyEventsNotifier.compareAndFireConfigEvents(origStrategyConfigs.get(dsName), saved.getStrategyConfig(dsName), new DSMapping(tplPath, new DsNameTarget(dsName))); + } + }); + } + + initOrigStrategyConfigs(saved); + } + } +} diff --git a/designer-base/src/main/java/com/fr/design/data/datapane/ESDStrategyConfigPane.java b/designer-base/src/main/java/com/fr/design/data/datapane/ESDStrategyConfigPane.java new file mode 100644 index 000000000..fb0996e8d --- /dev/null +++ b/designer-base/src/main/java/com/fr/design/data/datapane/ESDStrategyConfigPane.java @@ -0,0 +1,225 @@ +package com.fr.design.data.datapane; + +import com.fr.design.beans.BasicBeanPane; +import com.fr.design.gui.ibutton.UIRadioButton; +import com.fr.design.gui.icheckbox.UICheckBox; +import com.fr.design.gui.ilable.ActionLabel; +import com.fr.design.gui.ilable.UILabel; +import com.fr.design.gui.itextfield.UITextField; +import com.fr.design.layout.FRGUIPaneFactory; +import com.fr.design.utils.gui.GUICoreUtils; +import com.fr.esd.common.CacheConstants; +import com.fr.esd.core.strategy.config.StrategyConfig; +import com.fr.esd.core.strategy.config.StrategyConfigHelper; +import com.fr.esd.util.ESDUtils; +import com.fr.locale.InterProviderFactory; +import com.fr.log.FineLoggerFactory; +import com.fr.stable.StringUtils; + +import javax.swing.AbstractAction; +import javax.swing.JPanel; +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.Component; +import java.awt.Desktop; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.net.URI; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +/** + * @author rinoux + * @version 10.0 + * Created by rinoux on 2020/7/22 + */ +public class ESDStrategyConfigPane extends BasicBeanPane { + private UIRadioButton selectAutoUpdate; + private UIRadioButton selectBySchema; + private UICheckBox shouldEvolve; + private UILabel updateIntervalCheckTips; + private UITextField updateInterval; + private UITextField schemaTime; + private ActionLabel actionLabel; + private UILabel schemaTimeCheckTips; + private final boolean global; + private StrategyConfig strategyConfig; + + private static final String CRON_HELP_URL = "http://help.fanruan.com/finereport/doc-view-693.html"; + + public ESDStrategyConfigPane(boolean global) { + this.global = global; + init(); + } + + private void init() { + setLayout(FRGUIPaneFactory.createM_BorderLayout()); + + this.selectAutoUpdate = new UIRadioButton(InterProviderFactory.getDesignI18nProvider().i18nText("Fine-Design_ESD_Every_Interval")); + + this.updateInterval = new UITextField(4); + this.shouldEvolve = new UICheckBox(InterProviderFactory.getDesignI18nProvider().i18nText("Fine-Design_ESD_Auto_Evolved_Strategy"), false); + this.shouldEvolve.setEnabled(false); + this.updateIntervalCheckTips = new UILabel(InterProviderFactory.getDesignI18nProvider().i18nText("Fine-Design_ESD_Error_Interval_Format")); + this.updateIntervalCheckTips.setForeground(Color.RED); + this.updateIntervalCheckTips.setVisible(false); + + this.selectBySchema = new UIRadioButton(InterProviderFactory.getDesignI18nProvider().i18nText("Fine-Design_ESD_Cron")); + this.schemaTime = new UITextField(10); + this.schemaTimeCheckTips = new UILabel(InterProviderFactory.getDesignI18nProvider().i18nText("Fine-Design_ESD_Error_Time_Format")); + this.schemaTimeCheckTips.setVisible(false); + this.schemaTimeCheckTips.setForeground(Color.RED); + + + this.selectAutoUpdate.addActionListener(new AbstractAction() { + public void actionPerformed(ActionEvent e) { + ESDStrategyConfigPane.this.selectBySchema.setSelected(!ESDStrategyConfigPane.this.selectAutoUpdate.isSelected()); + ESDStrategyConfigPane.this.schemaTime.setEnabled(false); + ESDStrategyConfigPane.this.updateInterval.setEnabled(true); + ESDStrategyConfigPane.this.shouldEvolve.setEnabled(true); + } + }); + + this.selectBySchema.addActionListener(new AbstractAction() { + public void actionPerformed(ActionEvent e) { + ESDStrategyConfigPane.this.selectAutoUpdate.setSelected(!ESDStrategyConfigPane.this.selectBySchema.isSelected()); + ESDStrategyConfigPane.this.schemaTime.setEnabled(true); + ESDStrategyConfigPane.this.updateInterval.setEnabled(false); + ESDStrategyConfigPane.this.shouldEvolve.setEnabled(false); + } + }); + + JPanel pane = FRGUIPaneFactory.createVerticalTitledBorderPane(InterProviderFactory.getDesignI18nProvider().i18nText("Fine-Design_ESD_Cache_Update_Strategy")); + add(pane, BorderLayout.NORTH); + + JPanel row1 = GUICoreUtils.createFlowPane(new Component[]{ + this.selectAutoUpdate, + this.updateInterval, + new UILabel(InterProviderFactory.getDesignI18nProvider().i18nText("Fine-Design_ESD_Minute_Update_Cache")), + this.shouldEvolve, + this.updateIntervalCheckTips + }, 0, 5); + pane.add(row1); + + ActionLabel actionLabel = new ActionLabel(InterProviderFactory.getDesignI18nProvider().i18nText("Fine-Design_ESD_Cron_Help")); + actionLabel.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + try { + Desktop.getDesktop().browse(new URI(CRON_HELP_URL)); + } catch (Exception exp) { + FineLoggerFactory.getLogger().error(exp.getMessage(), e); + } + } + }); + + JPanel row2 = GUICoreUtils.createFlowPane(new Component[]{ + this.selectBySchema, + this.schemaTime, + actionLabel, + this.schemaTimeCheckTips + }, 0, 5); + pane.add(row2); + } + + + public void populateBean(StrategyConfig ob) { + + if (ob == null && !global) { + ob = StrategyConfigHelper.createStrategyConfig(true, false, true); + } + this.strategyConfig = ob; + + this.updateInterval.setText(ob.getUpdateInterval() <= 0 ? "0" : String.valueOf(ob.getUpdateInterval() / (double) CacheConstants.MILLIS_OF_MINUTE)); + this.schemaTime.setText(StringUtils.join(",", ob.getUpdateSchema().toArray(new String[0]))); + this.shouldEvolve.setSelected(ob.shouldEvolve()); + this.selectBySchema.setSelected(ob.isScheduleBySchema()); + this.selectAutoUpdate.setSelected(!ob.isScheduleBySchema()); + + if (global) { + //使用全局配置,禁用面板编辑 + disablePane(); + } else { + setSchemaEnable(!this.selectAutoUpdate.isSelected()); + } + } + + + private void disablePane() { + this.selectAutoUpdate.setEnabled(false); + this.updateInterval.setEnabled(false); + this.shouldEvolve.setEnabled(false); + + this.selectBySchema.setEnabled(false); + this.schemaTime.setEnabled(false); + } + + private void setSchemaEnable(boolean enable) { + this.updateInterval.setEnabled(!enable); + this.shouldEvolve.setEnabled(!enable); + + this.schemaTime.setEnabled(enable); + } + + + public StrategyConfig updateBean() { + StrategyConfig config = null; + if (!this.global) { + try { + //这里是new的config + config = this.strategyConfig.clone(); + + if (this.selectBySchema.isSelected()) { + List schemaTimes = new ArrayList<>(); + String text = this.schemaTime.getText(); + + if (ESDUtils.checkUpdateTimeSchema(text)) { + if (ESDUtils.isCronExpression(text)) { + schemaTimes.add(text); + } else { + Collections.addAll(schemaTimes, text.split(",")); + } + } else { + this.schemaTimeCheckTips.setVisible(true); + throw new IllegalArgumentException("update schema time format error."); + } + config.setScheduleBySchema(true); + config.setUpdateSchema(schemaTimes); + } else { + String interval = this.updateInterval.getText(); + if (checkUpdateInterval(interval)) { + long intervalMillis = StringUtils.isEmpty(interval) ? 0 : (long) (Double.parseDouble(interval) * CacheConstants.MILLIS_OF_MINUTE); + config.setUpdateInterval(intervalMillis); + } else { + this.updateIntervalCheckTips.setVisible(true); + throw new IllegalArgumentException("Error update interval format."); + } + + config.setShouldEvolve(this.shouldEvolve.isSelected()); + config.setScheduleBySchema(false); + } + } catch (CloneNotSupportedException e) { + FineLoggerFactory.getLogger().error(e.getMessage(), e); + } + } + + return config; + } + + + private boolean checkUpdateInterval(String intervalValue) { + try { + return !StringUtils.isEmpty(intervalValue) && !(Double.parseDouble(intervalValue) <= 0); + } catch (NumberFormatException e) { + FineLoggerFactory.getLogger().error(e.getMessage(), e); + } + + return false; + } + + + protected String title4PopupWindow() { + return InterProviderFactory.getDesignI18nProvider().i18nText("Fine-Design_ESD_Cache_Strategy_Config_Title"); + } +} 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 2356de6d4..9fd8cfa09 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,8 +1,8 @@ package com.fr.design.data.datapane; -import com.fr.base.BaseUtils; import com.fr.base.TableData; import com.fr.data.TableDataSource; +import com.fr.data.impl.DBTableData; import com.fr.data.impl.TableDataSourceDependent; import com.fr.design.DesignModelAdapter; import com.fr.design.ExtraDesignClassManager; @@ -11,52 +11,67 @@ import com.fr.design.constants.UIConstants; import com.fr.design.data.BasicTableDataTreePane; import com.fr.design.data.BasicTableDataUtils; import com.fr.design.data.DesignTableDataManager; +import com.fr.design.data.DesignerStrategyConfigUtils; import com.fr.design.data.tabledata.StoreProcedureWorkerListener; import com.fr.design.data.tabledata.tabledatapane.AbstractTableDataPane; +import com.fr.design.data.tabledata.tabledatapane.DBTableDataPane; import com.fr.design.data.tabledata.wrapper.AbstractTableDataWrapper; +import com.fr.design.data.tabledata.wrapper.TableDataWrapper; +import com.fr.design.data.tabledata.wrapper.TemplateTableDataWrapper; import com.fr.design.dialog.BasicDialog; -import com.fr.design.dialog.BasicPane; import com.fr.design.dialog.DialogActionAdapter; import com.fr.design.dialog.FineJOptionPane; +import com.fr.design.file.HistoryTemplateListCache; import com.fr.design.fun.TableDataPaneProcessor; import com.fr.design.gui.ibutton.UIHeadGroup; import com.fr.design.gui.icontainer.UIScrollPane; import com.fr.design.gui.itextfield.UITextField; import com.fr.design.gui.itoolbar.UIToolbar; +import com.fr.design.gui.itree.refreshabletree.ExpandMutableTreeNode; +import com.fr.design.i18n.Toolkit; import com.fr.design.icon.IconPathConstants; import com.fr.design.layout.FRGUIPaneFactory; import com.fr.design.mainframe.DesignerContext; +import com.fr.design.menu.LineSeparator; import com.fr.design.menu.MenuDef; import com.fr.design.menu.SeparatorDef; import com.fr.design.menu.ToolBarDef; +import com.fr.esd.core.strategy.config.StrategyConfig; +import com.fr.esd.core.strategy.config.StrategyConfigHelper; +import com.fr.esd.event.DSMapping; +import com.fr.esd.event.DsNameTarget; +import com.fr.esd.event.StrategyEventsNotifier; import com.fr.general.ComparatorUtils; -import com.fr.general.GeneralContext; +import com.fr.general.IOUtils; import com.fr.general.NameObject; -import com.fr.plugin.context.PluginContext; -import com.fr.plugin.injectable.PluginModule; -import com.fr.plugin.manage.PluginFilter; -import com.fr.plugin.observer.PluginEvent; -import com.fr.plugin.observer.PluginEventListener; +import com.fr.log.FineLoggerFactory; import com.fr.stable.core.PropertyChangeAdapter; +import org.jetbrains.annotations.NotNull; import javax.swing.BorderFactory; +import javax.swing.Icon; import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.SwingUtilities; +import javax.swing.SwingWorker; import javax.swing.ToolTipManager; +import javax.swing.tree.TreePath; import java.awt.BorderLayout; import java.awt.GridLayout; import java.awt.dnd.DnDConstants; import java.awt.event.ActionEvent; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; +import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.Iterator; +import java.util.List; import java.util.Map; +import java.util.concurrent.ExecutionException; public class TableDataTreePane extends BasicTableDataTreePane { - private static TableDataTreePane singleton = new TableDataTreePane(); + private static final TableDataTreePane singleton = new TableDataTreePane(); public static final int PLUGIN_LISTENER_PRIORITY = 1; @@ -78,31 +93,42 @@ public class TableDataTreePane extends BasicTableDataTreePane { } private TableDataSourceOP op; - private TableDataTree dataTree; + private TableDataTree tableDataTree; private EditAction editAction; private RemoveAction removeAction; + private EsdOnAction esdAction; + private EsdOffAction esdOffAction; private PreviewTableDataAction previewTableDataAction; private TableDataTreePane() { + initPane(); + } + + private void initPane() { this.setLayout(new BorderLayout(4, 0)); this.setBorder(null); - dataTree = new TableDataTree(); - ToolTipManager.sharedInstance().registerComponent(dataTree); + + //TableDataTree + tableDataTree = new TableDataTree(); + ToolTipManager.sharedInstance().registerComponent(tableDataTree); ToolTipManager.sharedInstance().setDismissDelay(3000); ToolTipManager.sharedInstance().setInitialDelay(0); - addMenuDef = new MenuDef(com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Basic_Action_Add")); - addMenuDef.setIconPath(IconPathConstants.ADD_POPMENU_ICON_PATH); + + addMenuDef = new MenuDef(Toolkit.i18nText("Fine-Design_Basic_Action_Add")); + addMenuDef.setIconPath(IconPathConstants.ADD_POPMENU_ICON_PATH); createAddMenuDef(); - // 创建插件监听 - createPluginListener(); editAction = new EditAction(); removeAction = new RemoveAction(); - previewTableDataAction = new PreviewTableDataAction(dataTree); + previewTableDataAction = new PreviewTableDataAction(tableDataTree); connectionTableAction = new ConnectionTableAction(); + esdAction = new EsdOnAction(); + esdOffAction = new EsdOffAction(); + + ToolBarDef toolbarDef = new ToolBarDef(); - toolbarDef.addShortCut(addMenuDef, SeparatorDef.DEFAULT, editAction, removeAction, SeparatorDef.DEFAULT, previewTableDataAction, connectionTableAction); + toolbarDef.addShortCut(addMenuDef, SeparatorDef.DEFAULT, editAction, removeAction, SeparatorDef.DEFAULT, previewTableDataAction, connectionTableAction, esdAction, esdOffAction); UIToolbar toolBar = ToolBarDef.createJToolBar(); toolBar.setBorder(BorderFactory.createMatteBorder(0, 0, 1, 0, UIConstants.TOOLBAR_BORDER_COLOR)); toolBar.setBorderPainted(true); @@ -112,115 +138,152 @@ public class TableDataTreePane extends BasicTableDataTreePane { toolbarPane.add(toolBar, BorderLayout.CENTER); this.add(toolbarPane, BorderLayout.NORTH); - UIScrollPane scrollPane = new UIScrollPane(dataTree); + UIScrollPane scrollPane = new UIScrollPane(tableDataTree); scrollPane.setBorder(null); - initbuttonGroup(); + initButtonGroup(); JPanel jPanel = new JPanel(new BorderLayout(0, 0)); JPanel buttonPane = new JPanel(new GridLayout()); buttonPane.add(buttonGroup, BorderLayout.CENTER); jPanel.add(buttonPane, BorderLayout.NORTH); jPanel.add(scrollPane, BorderLayout.CENTER); this.add(jPanel, BorderLayout.CENTER); - dataTree.addMouseListener(new MouseAdapter() { + tableDataTree.addMouseListener(new MouseAdapter() { @Override public void mousePressed(MouseEvent e) { checkButtonEnabled(); } }); - dataTree.addKeyListener(getTableTreeNodeListener(editAction, previewTableDataAction, removeAction, op, dataTree)); + tableDataTree.addKeyListener(getTableTreeNodeListener(editAction, previewTableDataAction, removeAction, op, tableDataTree)); // TreeCellEditor - dataTree.setEditable(true); - TableDataTreeCellEditor treeCellEditor = new TableDataTreeCellEditor(new UITextField(), dataTree, this); + tableDataTree.setEditable(true); + TableDataTreeCellEditor treeCellEditor = new TableDataTreeCellEditor(new UITextField(), tableDataTree, this); treeCellEditor.addCellEditorListener(treeCellEditor); - dataTree.setCellEditor(treeCellEditor); - new TableDataTreeDragSource(dataTree, DnDConstants.ACTION_COPY); + tableDataTree.setCellEditor(treeCellEditor); + new TableDataTreeDragSource(tableDataTree, DnDConstants.ACTION_COPY); checkButtonEnabled(); } - private void createPluginListener() { - - //菜单栏监听 - GeneralContext.listenPluginRunningChanged(new PluginEventListener(PLUGIN_LISTENER_PRIORITY) { - - @Override - public void on(PluginEvent event) { - - addMenuDef.clearShortCuts(); - createAddMenuDef(); - } - }, new PluginFilter() { - - @Override - public boolean accept(PluginContext context) { - - return context.contain(PluginModule.ExtraDesign); - } - }); - } - - - protected void checkButtonEnabled() { - super.checkButtonEnabled(editAction, previewTableDataAction, removeAction, op, dataTree); - } - /** * 刷新 */ + @Override public void refreshDockingView() { populate(new TableDataSourceOP(tc)); this.checkButtonEnabled(); } - protected void initbuttonGroup() { -// Icon[] iconArray = {BaseUtils.readIcon("/com/fr/design/images/data/datasource.png"), BaseUtils.readIcon("/com/fr/design/images/data/dock/serverdatabase.png")}; - final Integer[] modeArray = {TEMPLATE_TABLE_DATA, SERVER_TABLE_DATA}; - String[] textArray = {com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Basic_Tabledata_Source_Type_Template"), com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Basic_DS_Server_TableData")}; - buttonGroup = new UIHeadGroup(textArray) { - public void tabChanged(int index) { - if (op != null) { - op.setDataMode(modeArray[buttonGroup.getSelectedIndex()]); - addMenuDef.setEnabled(modeArray[buttonGroup.getSelectedIndex()] == TEMPLATE_TABLE_DATA ? true : false); - refreshDockingView(); - } + + @Override + public final void createAddMenuDef() { + TableDataNameObjectCreator[] creators = TableDataCreatorProducer.getInstance().createReportTableDataCreator(); + for (final TableDataNameObjectCreator creator : creators) { + if (creator.shouldInsertSeparator()) { + addMenuDef.addShortCut(new LineSeparator()); } - }; - buttonGroup.setNeedLeftRightOutLine(false); + + addMenuDef.addShortCut(new AddAction() { + @Override + protected String getTDName() { + return creator.menuName(); + } + + @Override + protected Icon getTDIcon() { + return creator.menuIcon(); + } + + @Override + protected String getNamePrefix() { + return creator.getPrefix(); + } + + @Override + protected TemplateTableDataWrapper getTableDataInstance() { + TableData tableData = (TableData) creator.createObject(); + + //新建的DBTableData单独换为DBTableData + if (tableData instanceof DBTableData) { + tableData = new DBTableData(); + String dsName = createDsName(creator.getPrefix()); + ((DBTableData) tableData).setDsName(dsName); + } else { + // TODO: 2020/11/10 后期支持其它数据集,这里转化可以不用侵入修改TableDataCreatorProducer + } + return new TemplateTableDataWrapper(tableData); + } + }); + } } /** * 编辑面板 * - * @param uPanel 面板 - * @param originalName 原始名字 + * @param tableDataPane 面板 + * @param originalName 原始名字 */ - public void dgEdit(final AbstractTableDataPane uPanel, String originalName, boolean isUpdate) { - uPanel.addStoreProcedureWorkerListener(new StoreProcedureWorkerListener() { + @Override + public void dgEdit(final AbstractTableDataPane tableDataPane, String originalName, boolean isUpdate) { + tableDataPane.addStoreProcedureWorkerListener(new StoreProcedureWorkerListener() { public void fireDoneAction() { - if (dataTree.getSelectionPath() == null) { - dataTree.refresh(); + if (tableDataTree.getSelectionPath() == null) { + tableDataTree.refresh(); } else { - Object object = dataTree.getSelectionPath().getLastPathComponent(); - int[] rows = dataTree.getSelectionRows(); - dataTree.refreshChildByName(object.toString()); - dataTree.setSelectionRows(rows); + Object object = tableDataTree.getSelectionPath().getLastPathComponent(); + int[] rows = tableDataTree.getSelectionRows(); + tableDataTree.refreshChildByName(object.toString()); + tableDataTree.setSelectionRows(rows); } } }); - final BasicPane.NamePane nPanel = uPanel.asNamePane(); - nPanel.setObjectName(originalName); + final NamePane tdNamePanel = tableDataPane.asNamePane(); + tdNamePanel.setObjectName(originalName); + final String oldName = originalName; - final BasicDialog dg; allDSNames = DesignTableDataManager.getAllDSNames(tc.getBook()); - dg = nPanel.showLargeWindow(SwingUtilities.getWindowAncestor(TableDataTreePane.this), new DialogActionAdapter() { + + final BasicDialog dg = tdNamePanel.showLargeWindow(SwingUtilities.getWindowAncestor(TableDataTreePane.this), new DialogActionAdapter() { + @Override public void doOk() { DesignTableDataManager.setThreadLocal(DesignTableDataManager.NO_PARAMETER); - tc.renameTableData(oldName, nPanel.getObjectName(), false); + tc.renameTableData(oldName, tdNamePanel.getObjectName(), false); TableDataSource tds = tc.getBook(); - TableData td = uPanel.updateBean(); + TableData td = tableDataPane.updateBean(); if (td instanceof TableDataSourceDependent) { ((TableDataSourceDependent) td).setTableDataSource(tds); } - String tdName = nPanel.getObjectName(); + String tdName = tdNamePanel.getObjectName(); + + + //模版还没保存的时候不知道后缀,不能设置到策略配置里面 + if (tableDataPane instanceof DBTableDataPane) { + StrategyConfig editingConfig = ((DBTableDataPane) tableDataPane).updateStrategyConfig(); + if (editingConfig != null) { + editingConfig.setDsName(tdName); + if (!ComparatorUtils.equals(oldName, tdName)) { + //重命名 + DesignerStrategyConfigUtils.removeStrategyConfig(oldName); + } + + DesignerStrategyConfigUtils.addStrategyConfig(editingConfig); + } + ((DBTableData) td).setDsName(tdName); + } + + + //服务器数据集不会走这里,入口不一样 + /*if (tableDataPane instanceof ServerDBTableDataPane) { + StrategyConfig editingConfig = ((ServerDBTableDataPane) tableDataPane).updateStrategyConfig(); + if (editingConfig != null) { + editingConfig.setDsName(tdName); + if (!ComparatorUtils.equals(oldName, tdName)) { + //重命名 + DesignerStrategyConfigUtils.removeServerStrategyConfig(oldName); + } + + DesignerStrategyConfigUtils.addServerStrategyConfig(editingConfig); + } + }*/ + tds.putTableData(tdName, td); Map map = new HashMap(); if (!ComparatorUtils.equals(oldName, tdName)) { @@ -228,82 +291,35 @@ public class TableDataTreePane extends BasicTableDataTreePane { } fireDSChanged(map); tc.fireTargetModified(); - tc.tableDataParametersChanged(oldName, tdName, td); - int[] rows = dataTree.getSelectionRows(); - dataTree.refreshChildByName(tdName); - dataTree.setSelectionRows(rows); + tc.parameterChanged(); + int[] rows = tableDataTree.getSelectionRows(); + tableDataTree.refreshChildByName(tdName); + tableDataTree.setSelectionRows(rows); + + + //单独编辑数据集关闭,修改缓存配置状态,刷新下一键开启/关闭按钮 + checkButtonEnabled(); + } + + @Override + public void doCancel() { + super.doCancel(); } }); - nPanel.addPropertyChangeListener(new PropertyChangeAdapter() { + tdNamePanel.addPropertyChangeListener(new PropertyChangeAdapter() { @Override public void propertyChange() { - doPropertyChange(dg, nPanel, oldName); + doPropertyChange(dg, tdNamePanel, oldName); } }); dg.setVisible(true); } - private class EditAction extends UpdateAction { - public EditAction() { - this.setName(com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Basic_Edit")); - this.setMnemonic('E'); - this.setSmallIcon("/com/fr/design/images/control/edit"); - } - - public void actionPerformed(ActionEvent e) { - final NameObject selectedNO = dataTree.getSelectedNameObject(); - if (selectedNO == null) { - return; - } - DesignTableDataManager.removeSelectedColumnNames(selectedNO.getName()); - dgEdit(((AbstractTableDataWrapper) selectedNO.getObject()).creatTableDataPane(), selectedNO.getName(), false); - } - } - - private class RemoveAction extends UpdateAction { - - public RemoveAction() { - this.setName(com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Basic_Remove")); - this.setMnemonic('R'); - this.setSmallIcon("/com/fr/design/images/control/remove"); - } - - @Override - public void actionPerformed(ActionEvent e) { - NameObject selectedNO = dataTree.getSelectedNameObject(); - - if (selectedNO == null) { - return; - } - - int returnVal = FineJOptionPane.showConfirmDialog(DesignerContext.getDesignerFrame(), com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Basic_Utils_Are_You_Sure_To_Remove_The_Selected_Item") + ":" + selectedNO.getName() + "?", - com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Basic_Remove"), JOptionPane.OK_CANCEL_OPTION, JOptionPane.QUESTION_MESSAGE); - if (returnVal == JOptionPane.OK_OPTION) { - // richer:这个地方为什么要在DataSourceTree里面去remove呢?多此一举吧 - op.removeAction(selectedNO.getName()); - dataTree.refresh(); - // Richie:默认最后一行获得焦点. - dataTree.requestFocus(); - dataTree.setSelectionRow(dataTree.getRowCount() - 1); - fireDSChanged(); - checkButtonEnabled(); - DesignTableDataManager.removeSelectedColumnNames(selectedNO.getName()); - } - } - } - - private void populate(TableDataSourceOP op) { - this.op = op; - dataTree.populate(op); - checkButtonEnabled(); - } - - - /** - * @return - */ - public TableDataTree getDataTree() { - return dataTree; + @Override + public void removeTableData(String sourceName) { + TableDataSource tds = this.tc.getBook(); + tds.removeTableData(sourceName); + this.tableDataTree.refresh(); } /** @@ -312,6 +328,7 @@ public class TableDataTreePane extends BasicTableDataTreePane { * @param srcName 数据集来源(比如报表块,就是报表块的名称) * @param tableDataSource 数据集 */ + @Override public Map addTableData(String srcName, TableDataSource tableDataSource, boolean isCover) { Map tdNameMap = new HashMap<>(); allDSNames = DesignTableDataManager.getAllDSNames(tc.getBook()); @@ -330,29 +347,55 @@ public class TableDataTreePane extends BasicTableDataTreePane { } } tc.parameterChanged(); - dataTree.refresh(); + tableDataTree.refresh(); return Collections.unmodifiableMap(tdNameMap); } + /** + * 合并数据集 + * + * @param srcName 数据集来源(比如报表块,就是报表块的名称) + * @param tableDataSource 数据集 + */ @Override - public void removeTableData(String sourceName) { + public Map addTableData(String srcName, TableDataSource tableDataSource) { + Map tdNameMap = new HashMap<>(); + allDSNames = DesignTableDataManager.getAllDSNames(tc.getBook()); + DesignTableDataManager.setThreadLocal(DesignTableDataManager.NO_PARAMETER); TableDataSource tds = tc.getBook(); - tds.removeTableData(sourceName); - dataTree.refresh(); + Iterator tdIterator = tableDataSource.getTableDataNameIterator(); + while (tdIterator.hasNext()) { + String tdName = tdIterator.next(); + String oldName = tdName; + TableData td = tableDataSource.getTableData(tdName); + if (tds.getTableData(tdName) != null || isDsNameRepeaded(tdName)) { + //如果有同名的就拼上来源名称 + tdName = srcName + tdName; + } + int i = 0; + while (tds.getTableData(tdName) != null) { + i++;//如果拼上名字后依然已经存在就加编号 + tdName += i; + } + tds.putTableData(tdName, td); + if (!ComparatorUtils.equals(oldName, tdName)) { + tdNameMap.put(oldName, tdName); + } + } + tc.parameterChanged(); + tableDataTree.refresh(); + return Collections.unmodifiableMap(tdNameMap); } - - - + @Override public void addDataPane(final AbstractTableDataPane uPanel, String paneName) { final NamePane nPanel = uPanel.asNamePane(); nPanel.setObjectName(paneName); - final String oldName = paneName; allDSNames = DesignTableDataManager.getAllDSNames(tc.getBook()); DesignTableDataManager.setThreadLocal(DesignTableDataManager.NO_PARAMETER); - tc.renameTableData(oldName, nPanel.getObjectName(), false); + tc.renameTableData(paneName, nPanel.getObjectName(), false); TableDataSource tds = tc.getBook(); TableData td = uPanel.updateBean(); if (td instanceof TableDataSourceDependent) { @@ -361,14 +404,409 @@ public class TableDataTreePane extends BasicTableDataTreePane { String tdName = nPanel.getObjectName(); tds.putTableData(tdName, td); Map map = new HashMap(); - if (!ComparatorUtils.equals(oldName, tdName)) { - map.put(oldName, tdName); + if (!ComparatorUtils.equals(paneName, tdName)) { + map.put(paneName, tdName); } fireDSChanged(map); tc.fireTargetModified(); - tc.tableDataParametersChanged(oldName, tdName, td); - int[] rows = dataTree.getSelectionRows(); - dataTree.refreshChildByName(tdName); - dataTree.setSelectionRows(rows); + tc.parameterChanged(); + int[] rows = tableDataTree.getSelectionRows(); + tableDataTree.refreshChildByName(tdName); + tableDataTree.setSelectionRows(rows); + } + + @Override + public TableDataTree getDataTree() { + return this.tableDataTree; + } + + private void populate(TableDataSourceOP op) { + this.op = op; + tableDataTree.populate(op); + checkButtonEnabled(); + } + + + private void resetAddMenuDef() { + this.addMenuDef.clearShortCuts(); + this.createAddMenuDef(); + } + + private void checkButtonEnabled() { + super.checkButtonEnabled(editAction, previewTableDataAction, removeAction, op, tableDataTree); + this.checkESDComponentsEnabled(); + } + + + private void checkESDComponentsEnabled() { + if (buttonGroup.getSelectedIndex() == 1) { + //切换到服务器数据集的tab,禁用 + esdAction.setEnabled(false); + esdOffAction.setEnabled(false); + return; + } + + new SwingWorker() { + @Override + protected ButtonStatus doInBackground() throws Exception { + ButtonStatus btnStatus = new ButtonStatus(); + Map statusMap = checkCanBeEsdBatchEnableStatus(); + if (statusMap.size() == 0) { + //没有db数据集 + return btnStatus; + } else { + int canTurnOnCount = 0; + int canTurnOffCount = 0; + for (ESDStatus status : statusMap.values()) { + switch (status) { + case GLOBAL_ON: + case GLOBAL_OFF: + case SINGLE_OFF: + canTurnOnCount++; + break; + case SINGLE_ON: + canTurnOffCount++; + break; + default: + break; + } + } + btnStatus.setOnStatus(canTurnOnCount > 0); + btnStatus.setOffStatus(canTurnOffCount > 0); + } + return btnStatus; + } + + @Override + protected void done() { + try { + ButtonStatus buttonStatus = get(); + esdAction.setEnabled(buttonStatus.isOnStatus()); + esdOffAction.setEnabled(buttonStatus.isOffStatus()); + } catch (InterruptedException | ExecutionException e) { + FineLoggerFactory.getLogger().error("get esd status of ds error for " + e.getMessage(), e); + } + } + }.execute(); + } + + private static class ButtonStatus { + private boolean onStatus = false; + private boolean offStatus = false; + + public boolean isOnStatus() { + return onStatus; + } + + public void setOnStatus(boolean onStatus) { + this.onStatus = onStatus; + } + + public boolean isOffStatus() { + return offStatus; + } + + public void setOffStatus(boolean offStatus) { + this.offStatus = offStatus; + } + } + + //模版数据集 + private void initButtonGroup() { + final Integer[] modeArray = {TEMPLATE_TABLE_DATA, SERVER_TABLE_DATA}; + String[] textArray = {Toolkit.i18nText("Fine-Design_Basic_Tabledata_Source_Type_Template"), Toolkit.i18nText("Fine-Design_Basic_DS_Server_TableData")}; + buttonGroup = new UIHeadGroup(textArray) { + public void tabChanged(int index) { + if (op != null) { + op.setDataMode(modeArray[buttonGroup.getSelectedIndex()]); + addMenuDef.setEnabled(modeArray[buttonGroup.getSelectedIndex()] == TEMPLATE_TABLE_DATA); + refreshDockingView(); + + } + } + }; + buttonGroup.setNeedLeftRightOutLine(false); + } + + + private TableDataWrapper findTableDataWrapper(TreePath treePath) { + if (treePath != null) { + ExpandMutableTreeNode selectedTreeNode = (ExpandMutableTreeNode) treePath.getLastPathComponent(); + Object selectedUserObject = selectedTreeNode.getUserObject(); + NameObject nameObject = null; + if (selectedUserObject instanceof NameObject) { + nameObject = (NameObject) selectedUserObject; + } else { + selectedTreeNode = (ExpandMutableTreeNode) selectedTreeNode.getParent(); + selectedUserObject = selectedTreeNode.getUserObject(); + if (selectedUserObject instanceof NameObject) { + nameObject = (NameObject) selectedUserObject; + } + } + if (nameObject != null && nameObject.getObject() instanceof TableDataWrapper) { + return (TableDataWrapper) nameObject.getObject(); + } + } + + return null; + } + + private String getTplPath() { + return HistoryTemplateListCache.getInstance().getCurrentEditingTemplate().getPath(); + } + + enum ESDStatus { + GLOBAL_ON, + GLOBAL_OFF, + SINGLE_ON, + SINGLE_OFF; + } + + + private Map checkCanBeEsdBatchEnableStatus() { + //<数据集, 是否可以被单独开启> + Map result = new HashMap<>(); + for (int i = 0; i < tableDataTree.getRowCount(); i++) { + TreePath treePath = tableDataTree.getPathForRow(i); + + TableDataWrapper wrapper = findTableDataWrapper(treePath); + if (wrapper.getTableData() instanceof DBTableData) { + DBTableData td = (DBTableData) wrapper.getTableData(); + + StrategyConfig config = DesignerStrategyConfigUtils.getStrategyConfig(wrapper.getTableDataName()); + if (config == null) { + result.put(td, td.isShare() ? ESDStatus.SINGLE_ON : ESDStatus.SINGLE_OFF); + } else { + if (config.isUseGlobal()) { + if (StrategyConfigHelper.recommended(getTplPath())) { + result.put(td, ESDStatus.GLOBAL_ON); + } else { + result.put(td, ESDStatus.GLOBAL_OFF); + } + } else { + if (config.enabled()) { + result.put(td, ESDStatus.SINGLE_ON); + } else { + result.put(td, ESDStatus.SINGLE_OFF); + } + } + } + } + } + + return result; + } + + + private class EsdOnAction extends AbstractESDAction { + + @Override + public String getName() { + return Toolkit.i18nText("Fine-Design_ESD_Batch_Enable"); + } + + @Override + public Icon getIcon() { + return IOUtils.readIcon("/com/fr/design/images/control/batch_esd_on.png"); + } + + @Override + public void doWithTableDataWrapper(TableDataWrapper wrapper) { + String dsName = wrapper.getTableDataName(); + if (wrapper.getTableData() instanceof DBTableData) { + StrategyConfig strategyConfig = getOrCreateStrategyConfig(dsName); + strategyConfig.setEnable(true); + strategyConfig.setUseGlobal(false); + DesignerStrategyConfigUtils.addStrategyConfig(strategyConfig); + DesignerContext.getDesignerFrame().getSelectedJTemplate().fireTargetModified(); + } + } + } + + private @NotNull StrategyConfig getOrCreateStrategyConfig(String dsName) { + StrategyConfig config = DesignerStrategyConfigUtils.getStrategyConfig(dsName); + + if (config == null) { + config = StrategyConfigHelper.createStrategyConfig(true); + config.setDsName(dsName); + } + + return config; + } + + + private String createDsName(String prefix) { + int count = 1; + allDSNames = DesignTableDataManager.getAllDSNames(tc.getBook()); + while (isDsNameRepeaded(prefix + count)) { + count++; + } + return prefix + count; + } + + + private class EsdOffAction extends AbstractESDAction { + + @Override + public String getName() { + return Toolkit.i18nText("Fine-Design_ESD_Batch_Disable"); + } + + @Override + public Icon getIcon() { + return IOUtils.readIcon("/com/fr/design/images/control/batch_esd_off.png"); + } + + @Override + public void doWithTableDataWrapper(TableDataWrapper wrapper) { + String dsName = wrapper.getTableDataName(); + if (wrapper.getTableData() instanceof DBTableData) { + ((DBTableData) wrapper.getTableData()).setShare(false); + StrategyConfig strategyConfig = getOrCreateStrategyConfig(dsName); + strategyConfig.setEnable(false); + strategyConfig.setUseGlobal(false); + DesignerStrategyConfigUtils.addStrategyConfig(strategyConfig); + DesignerContext.getDesignerFrame().getSelectedJTemplate().fireTargetModified(); + } + } + } + + private abstract class AbstractESDAction extends UpdateAction { + + public abstract String getName(); + + public abstract Icon getIcon(); + + public abstract void doWithTableDataWrapper(TableDataWrapper tableDataWrapper); + + public AbstractESDAction() { + this.setName(getName()); + this.setMnemonic('R'); + this.setSmallIcon(getIcon()); + } + + @Override + public void actionPerformed(ActionEvent e) { + List tableDataWrapperList = new ArrayList<>(); + + for (int i = 0; i < tableDataTree.getRowCount(); i++) { + TreePath treePath = tableDataTree.getPathForRow(i); + TableDataWrapper wrapper = findTableDataWrapper(treePath); + if (wrapper != null) { + tableDataWrapperList.add(wrapper); + } + } + if (tableDataWrapperList.isEmpty()) { + return; + } + new SwingWorker() { + @Override + protected Void doInBackground() throws Exception { + for (TableDataWrapper tableDataWrapper : tableDataWrapperList) { + doWithTableDataWrapper(tableDataWrapper); + } + return null; + } + + @Override + protected void done() { + refreshDockingView(); + } + }.execute(); + } + } + + + /** + * 新建数据集 + *

+ * 抽象化,支持不同类型数据集创建 + */ + private abstract class AddAction extends UpdateAction { + + protected abstract String getTDName(); + + protected abstract Icon getTDIcon(); + + protected abstract String getNamePrefix(); + + protected abstract TemplateTableDataWrapper getTableDataInstance(); + + public AddAction() { + this.setName(this.getTDName()); + this.setSmallIcon(this.getTDIcon()); + } + + @Override + public void actionPerformed(ActionEvent e) { + dgEdit(getTableDataInstance().creatTableDataPane(), createDsName(getNamePrefix()), false); + } + } + + + private class EditAction extends UpdateAction { + public EditAction() { + this.setName(Toolkit.i18nText("Fine-Design_Basic_Edit")); + this.setMnemonic('E'); + this.setSmallIcon(IOUtils.readIcon(IconPathConstants.TD_EDIT_ICON_PATH)); + } + + @Override + public void actionPerformed(ActionEvent e) { + final NameObject selectedNO = tableDataTree.getSelectedNameObject(); + if (selectedNO == null) { + return; + } + + String dsName = selectedNO.getName(); + DesignTableDataManager.removeSelectedColumnNames(dsName); + + AbstractTableDataWrapper wrapper = (AbstractTableDataWrapper) selectedNO.getObject(); + + AbstractTableDataPane tableDataPane = wrapper.creatTableDataPane(); + + //下面创建creatTableDataPane后会直接populate,所以populate时不能用后设置的一些参数,比如name + dgEdit(tableDataPane, dsName, false); + } + } + + private class RemoveAction extends UpdateAction { + + public RemoveAction() { + this.setName(Toolkit.i18nText("Fine-Design_Basic_Remove")); + this.setMnemonic('R'); + this.setSmallIcon(IOUtils.readIcon(IconPathConstants.TD_REMOVE_ICON_PATH)); + } + + @Override + public void actionPerformed(ActionEvent e) { + NameObject selectedNO = tableDataTree.getSelectedNameObject(); + + if (selectedNO == null) { + return; + } + + int returnVal = FineJOptionPane.showConfirmDialog(DesignerContext.getDesignerFrame(), Toolkit.i18nText("Fine-Design_Basic_Utils_Are_You_Sure_To_Remove_The_Selected_Item") + ":" + selectedNO.getName() + "?", + Toolkit.i18nText("Fine-Design_Basic_Remove"), JOptionPane.OK_CANCEL_OPTION, JOptionPane.QUESTION_MESSAGE); + if (returnVal == JOptionPane.OK_OPTION) { + // richer:这个地方为什么要在DataSourceTree里面去remove呢?多此一举吧 + op.removeAction(selectedNO.getName()); + tableDataTree.refresh(); + // Richie:默认最后一行获得焦点. + tableDataTree.requestFocus(); + tableDataTree.setSelectionRow(tableDataTree.getRowCount() - 1); + fireDSChanged(); + checkButtonEnabled(); + + //删掉缓存配置 + DesignerStrategyConfigUtils.removeStrategyConfig(selectedNO.getName()); + + // 如果一个模版是平台开启,这个数据集的配置不会存xml,预览模版时直接从全局配置copy,这样 + // 导致删除的时候StrategyConfigsAttrSavedHook没有通过前后配置比较感知数据集被删除,因此不会发出事件让其失效 + // 这里额外发出一次数据集修改事件 + StrategyEventsNotifier.modifyDataSet(new DSMapping(getTplPath(), new DsNameTarget(selectedNO.getName()))); + DesignTableDataManager.removeSelectedColumnNames(selectedNO.getName()); + } + } } } diff --git a/designer-base/src/main/java/com/fr/design/data/tabledata/tabledatapane/DBTableDataPane.java b/designer-base/src/main/java/com/fr/design/data/tabledata/tabledatapane/DBTableDataPane.java index 6065d0485..16b7e9fa1 100644 --- a/designer-base/src/main/java/com/fr/design/data/tabledata/tabledatapane/DBTableDataPane.java +++ b/designer-base/src/main/java/com/fr/design/data/tabledata/tabledatapane/DBTableDataPane.java @@ -1,6 +1,5 @@ package com.fr.design.data.tabledata.tabledatapane; -import com.fr.base.BaseUtils; import com.fr.base.Parameter; import com.fr.base.ParameterHelper; import com.fr.data.core.db.TableProcedure; @@ -13,15 +12,19 @@ import com.fr.design.ExtraDesignClassManager; import com.fr.design.actions.UpdateAction; import com.fr.design.border.UIRoundedBorder; import com.fr.design.constants.UIConstants; +import com.fr.design.data.DesignerStrategyConfigUtils; +import com.fr.design.data.datapane.ESDStrategyConfigPane; import com.fr.design.data.datapane.connect.ConnectionTableProcedurePane; -import com.fr.design.data.datapane.connect.ConnectionTableProcedurePane.DoubleClickSelectedNodeOnTreeListener; import com.fr.design.data.datapane.preview.PreviewTablePane; import com.fr.design.data.datapane.sqlpane.SQLEditPane; import com.fr.design.dialog.BasicDialog; import com.fr.design.dialog.BasicPane; import com.fr.design.dialog.DialogActionAdapter; +import com.fr.design.file.HistoryTemplateListCache; import com.fr.design.fun.DBTableDataMenuHandler; +import com.fr.design.gui.ibutton.UIButton; import com.fr.design.gui.icheckbox.UICheckBox; +import com.fr.design.gui.icombobox.UIComboBox; import com.fr.design.gui.ilable.UILabel; import com.fr.design.gui.itableeditorpane.ParameterTableModel; import com.fr.design.gui.itableeditorpane.UITableEditAction; @@ -29,18 +32,27 @@ import com.fr.design.gui.itableeditorpane.UITableEditorPane; import com.fr.design.gui.itoolbar.UIToolbar; import com.fr.design.gui.syntax.ui.rsyntaxtextarea.SyntaxConstants; import com.fr.design.gui.syntax.ui.rtextarea.RTextScrollPane; +import com.fr.design.i18n.Toolkit; import com.fr.design.mainframe.DesignerContext; import com.fr.design.menu.SeparatorDef; import com.fr.design.menu.ToolBarDef; import com.fr.design.utils.ParameterUtils; import com.fr.design.utils.gui.GUICoreUtils; +import com.fr.esd.core.strategy.config.StrategyConfig; +import com.fr.esd.core.strategy.config.StrategyConfigHelper; +import com.fr.esd.event.DSMapping; +import com.fr.esd.event.DsNameTarget; +import com.fr.esd.event.StrategyEventsNotifier; +import com.fr.esd.event.xml.XMLSavedHook; import com.fr.general.ComparatorUtils; +import com.fr.general.IOUtils; import com.fr.general.sql.SqlUtils; import com.fr.log.FineLoggerFactory; import com.fr.script.Calculator; import com.fr.stable.ArrayUtils; import com.fr.stable.ParameterProvider; import com.fr.stable.StringUtils; +import com.fr.workspace.WorkContext; import javax.swing.BorderFactory; import javax.swing.Box; @@ -55,6 +67,7 @@ import java.awt.BorderLayout; import java.awt.Color; import java.awt.Dimension; import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; import java.awt.event.FocusEvent; import java.awt.event.FocusListener; import java.awt.event.KeyEvent; @@ -62,25 +75,39 @@ import java.awt.event.KeyListener; import java.util.ArrayList; import java.util.List; +/** + * @author rinoux + * @version 10.0 + * Created by rinoux on 2020/7/22 + */ public class DBTableDataPane extends AbstractTableDataPane { + private static final int BOTTOM = 6; - private static final String PREVIEW_BUTTON = com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Basic_Preview"); - private static final String REFRESH_BUTTON = com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Basic_Refresh"); + private static final String PREVIEW_BUTTON = Toolkit.i18nText("Fine-Design_Basic_Preview"); + private static final String REFRESH_BUTTON = Toolkit.i18nText("Fine-Design_Basic_Refresh"); private ConnectionTableProcedurePane connectionTableProcedurePane; private UITableEditorPane editorPane; - private DBTableDataMenuHandler dbTableDataMenuHandler; private SQLEditPane sqlTextPane; - private UICheckBox isShareCheckBox; - private MaxMemRowCountPanel maxPanel; - private String pageQuery = null; + UIComboBox configFromList; + UICheckBox esdEnabled; + private UIButton esdSettingsBtn; + private UILabel barErrorTips; + private String pageQuery; private DBTableData dbTableData; + StrategyConfig strategyConfig; + + public DBTableDataPane() { + init(); + initMainSplitPane(); + } + private void init() { - this.setLayout(new BorderLayout(4, 4)); + setLayout(new BorderLayout(4, 4)); - sqlTextPane = new SQLEditPane(); - sqlTextPane.setSyntaxEditingStyle(SyntaxConstants.SYNTAX_STYLE_SQL); + this.sqlTextPane = new SQLEditPane(); + this.sqlTextPane.setSyntaxEditingStyle(SyntaxConstants.SYNTAX_STYLE_SQL); ParameterTableModel model = new ParameterTableModel() { @Override @@ -88,25 +115,27 @@ public class DBTableDataPane extends AbstractTableDataPane { return ArrayUtils.add(super.createDBTableAction(), new RefreshAction()); } }; - editorPane = new UITableEditorPane(model); + editorPane = new UITableEditorPane<>(model); + - // 左边的Panel,上面是选择DatabaseConnection的ComboBox,下面DatabaseConnection对应的Table - connectionTableProcedurePane = new ConnectionTableProcedurePane() { + this.connectionTableProcedurePane = new ConnectionTableProcedurePane() { @Override protected void filter(Connection connection, String conName, List nameList) { - - connection.addConnection(nameList, conName, new Class[]{JDBCDatabaseConnection.class, JNDIDatabaseConnection.class}); + connection.addConnection(nameList, conName, new Class[]{ + JDBCDatabaseConnection.class, + JNDIDatabaseConnection.class + }); } + @Override protected void addKeyMonitor() { - searchField.addKeyListener(new KeyListener() { - + this.searchField.addKeyListener(new KeyListener() { @Override public void keyTyped(KeyEvent e) { - //do nothing } + @Override public void keyPressed(KeyEvent e) { if (e.getKeyCode() == KeyEvent.VK_ENTER) { @@ -115,39 +144,39 @@ public class DBTableDataPane extends AbstractTableDataPane { } } + @Override public void keyReleased(KeyEvent e) { - //do nothing } }); } }; - connectionTableProcedurePane.addDoubleClickListener(new DoubleClickSelectedNodeOnTreeListener() { - + this.connectionTableProcedurePane.addDoubleClickListener(new ConnectionTableProcedurePane.DoubleClickSelectedNodeOnTreeListener() { @Override public void actionPerformed(TableProcedure target) { - Document document = sqlTextPane.getDocument(); + Document document = DBTableDataPane.this.sqlTextPane.getDocument(); try { - document.insertString(sqlTextPane.getCaretPosition(), target.toString(), null); + document.insertString(DBTableDataPane.this.sqlTextPane.getCaretPosition(), target.toString(), null); } catch (BadLocationException e) { FineLoggerFactory.getLogger().error(e.getMessage(), e); } - // 这里开始作色,本来可以给sqlTextPane添加DocumentListener来实现的, - // 后来发现insertString的时候,锁定了JTextPane,不能调用setXXX来作色,先这样了. - // sqlTextPane.syntaxTexts(); - sqlTextPane.requestFocus(); + + + DBTableDataPane.this.sqlTextPane.requestFocus(); } }); - sqlTextPane.addFocusListener(new FocusListener() { + + this.sqlTextPane.addFocusListener(new FocusListener() { @Override public void focusGained(FocusEvent e) { - // do nothing + barErrorTips.setVisible(false); } + @Override public void focusLost(FocusEvent e) { - if (isPreviewOrRefreshButton(e)) { - checkParameter(); + if (DBTableDataPane.this.isPreviewOrRefreshButton(e)) { + DBTableDataPane.this.checkParameter(); } } }); @@ -163,105 +192,189 @@ public class DBTableDataPane extends AbstractTableDataPane { northPane.add(editToolBar, BorderLayout.CENTER); northPane.setBorder(BorderFactory.createEmptyBorder(0, 0, BOTTOM, 0)); - RTextScrollPane sqlTextScrollPane = new RTextScrollPane(sqlTextPane); + RTextScrollPane sqlTextScrollPane = new RTextScrollPane(this.sqlTextPane); sqlTextScrollPane.setLineNumbersEnabled(true); sqlTextScrollPane.setBorder(new UIRoundedBorder(UIConstants.LINE_COLOR, 1, UIConstants.ARC)); sqlTextScrollPane.setPreferredSize(new Dimension(680, 600)); JPanel paraMeanPane = new JPanel(new BorderLayout()); paraMeanPane.setPreferredSize(new Dimension(680, 90)); - UILabel paraMean = new UILabel(com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Basic_Datasource_Param_DES")); + UILabel paraMean = new UILabel(Toolkit.i18nText("Fine-Design_Basic_Datasource_Param_DES")); paraMeanPane.add(paraMean, BorderLayout.CENTER); box.add(northPane); box.add(sqlTextScrollPane); box.add(paraMeanPane); - box.add(editorPane); + box.add(this.editorPane); JPanel sqlSplitPane = new JPanel(new BorderLayout(4, 4)); sqlSplitPane.add(box, BorderLayout.CENTER); box.setMinimumSize(new Dimension(300, 400)); - // 防止数据连接名过长时影响 split pane 分割效果 - // 本界面中给MaxMemRowCountPanel留的空间太小,造成MaxMemRowCountPanel显示异常,这边减小一点最小宽度,拉长MaxMemRowCountPanel - connectionTableProcedurePane.setMinimumSize(new Dimension(230, 400)); - connectionTableProcedurePane.setMaximumSize(new Dimension(500, 400)); - // 使用SplitPane - JSplitPane mainSplitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, true, connectionTableProcedurePane, sqlSplitPane); + + this.connectionTableProcedurePane.setMinimumSize(new Dimension(230, 400)); + this.connectionTableProcedurePane.setMaximumSize(new Dimension(500, 400)); + + JSplitPane mainSplitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, true, this.connectionTableProcedurePane, sqlSplitPane); mainSplitPane.setBorder(BorderFactory.createLineBorder(GUICoreUtils.getTitleLineBorderColor())); mainSplitPane.setOneTouchExpandable(true); - this.add(mainSplitPane, BorderLayout.CENTER); - } - - public DBTableDataPane() { - init(); - initMainSplitPane(); + add(mainSplitPane, BorderLayout.CENTER); } private boolean isPreviewOrRefreshButton(FocusEvent e) { if (e.getOppositeComponent() != null) { String name = e.getOppositeComponent().getName(); - return ComparatorUtils.equals(name, PREVIEW_BUTTON) || ComparatorUtils.equals(name, REFRESH_BUTTON); + return (ComparatorUtils.equals(name, PREVIEW_BUTTON) || ComparatorUtils.equals(name, REFRESH_BUTTON)); } return false; } + @Override protected String title4PopupWindow() { - return com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Basic_DS-Database_Query"); + return Toolkit.i18nText("Fine-Design_Basic_DS-Database_Query"); } - private void refresh() { + + private void refreshParameters() { String[] paramTexts = new String[2]; - paramTexts[0] = SqlUtils.tryPureSqlText(sqlTextPane.getText()); - paramTexts[1] = SqlUtils.tryPureSqlText(pageQuery); - List existParameterList = editorPane.update(); - Parameter[] ps = existParameterList == null ? new Parameter[0] : existParameterList.toArray(new Parameter[0]); - editorPane.populate(ParameterUtils.analyzeAndUnionParameters(paramTexts, ps)); + paramTexts[0] = SqlUtils.tryPureSqlText(this.sqlTextPane.getText()); + paramTexts[1] = SqlUtils.tryPureSqlText(this.pageQuery); + List existParameterList = this.editorPane.update(); + Parameter[] ps = (existParameterList == null) ? new Parameter[0] : existParameterList.toArray(new Parameter[0]); + + this.editorPane.populate(ParameterUtils.analyzeAndUnionParameters(paramTexts, ps)); } + private JToolBar createToolBar() { - // p:工具栏. ToolBarDef toolBarDef = new ToolBarDef(); toolBarDef.addShortCut(new PreviewAction()); toolBarDef.addShortCut(SeparatorDef.DEFAULT); toolBarDef.addShortCut(new EditPageQueryAction()); - dbTableDataMenuHandler = ExtraDesignClassManager.getInstance().getSingle(DBTableDataMenuHandler.MARK_STRING); - if (dbTableDataMenuHandler != null) { + this.dbTableDataMenuHandler = ExtraDesignClassManager.getInstance().getSingle("DBTableDataMenuHandler"); + if (this.dbTableDataMenuHandler != null) { toolBarDef.addShortCut(SeparatorDef.DEFAULT); - toolBarDef.addShortCut(dbTableDataMenuHandler.createQueryAction()); + toolBarDef.addShortCut(this.dbTableDataMenuHandler.createQueryAction()); } - isShareCheckBox = new UICheckBox(com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Basic_Is_Share_DBTableData")); - isShareCheckBox.setBackground(Color.WHITE); - maxPanel = new MaxMemRowCountPanel(); - maxPanel.setBorder(null); + + UILabel esdSettingsLabel = new UILabel(Toolkit.i18nText("Fine-Design_ESD_Cache_Settings")); + this.configFromList = new UIComboBox(ConfigFrom.values()); + this.esdEnabled = new UICheckBox(Toolkit.i18nText("Fine-Design_ESD_Enable_Cache")); + this.barErrorTips = new UILabel(); + this.barErrorTips.setForeground(Color.RED); + this.barErrorTips.setVisible(false); + + esdSettingsBtn = new UIButton(Toolkit.i18nText("Fine-Design_ESD_Strategy_Config")); + + esdSettingsBtn.addActionListener(new ActionListener() { + + @Override + public void actionPerformed(ActionEvent e) { + final boolean global = (DBTableDataPane.this.configFromList.getSelectedIndex() == DBTableDataPane.ConfigFrom.GLOBAL.getIndex()); + + final ESDStrategyConfigPane strategyConfigPane = new ESDStrategyConfigPane(global); + + StrategyConfig populateStrategyConfig = null; + if (global) { + populateStrategyConfig = StrategyConfigHelper.getGlobalStrategyConfig(); + } else { + populateStrategyConfig = strategyConfig; + } + + //显示对应的配置 + strategyConfigPane.populateBean(populateStrategyConfig); + + BasicDialog dlg = strategyConfigPane.showMediumWindow(DesignerContext.getDesignerFrame(), new DialogActionAdapter() { + @Override + public void doOk() { + super.doOk(); + if (!global) { + //点击策略配置面板的确定,重新设置策略配置 + strategyConfig = strategyConfigPane.updateBean(); + } + } + }); + dlg.setAlwaysOnTop(true); + dlg.setVisible(true); + } + }); + + this.configFromList.setSelectedIndex(DBTableDataPane.ConfigFrom.GLOBAL.getIndex()); + this.configFromList.addActionListener(e -> setEsdEnabled()); + UIToolbar editToolBar = ToolBarDef.createJToolBar(); toolBarDef.updateToolBar(editToolBar); - editToolBar.add(isShareCheckBox); - editToolBar.add(maxPanel); + + editToolBar.add(esdSettingsLabel); + editToolBar.add(this.configFromList); + editToolBar.add(this.esdEnabled); + editToolBar.add(this.esdSettingsBtn); + editToolBar.add(this.barErrorTips); return editToolBar; } + + /* private void showBarError(String errorTips) { + this.barErrorTips.setText(errorTips); + this.barErrorTips.setVisible(true); + } + + private boolean checkQuery(String errorTips) { + if (StringUtils.isEmpty(this.sqlTextPane.getText())) { + showBarError(errorTips); + return false; + } + + return true; + }*/ + + + /** + * 设置是否开启 + */ + protected void setEsdEnabled() { + boolean useIndividualConfig = configFromList.getSelectedIndex() == ConfigFrom.INDIVIDUAL.getIndex(); + if (useIndividualConfig) { + if (this.strategyConfig == null) { + //新建的数据集,选择单独时,可用但是不勾选 + this.esdEnabled.setSelected(false); + this.esdEnabled.setEnabled(true); + } else { + this.esdEnabled.setSelected(!this.strategyConfig.isUseGlobal() && this.strategyConfig.enabled()); + this.esdEnabled.setEnabled(true); + } + } else { + this.esdEnabled.setSelected(DesignerStrategyConfigUtils.isEditingTemplateRecommended()); + this.esdEnabled.setEnabled(false); + } + } + + + public StrategyConfig updateStrategyConfig() { + return this.strategyConfig; + } + private void checkParameter() { String[] paramTexts = new String[2]; - paramTexts[0] = sqlTextPane.getText(); - paramTexts[1] = pageQuery; + paramTexts[0] = this.sqlTextPane.getText(); + paramTexts[1] = this.pageQuery; Parameter[] parameters = ParameterHelper.analyze4Parameters(paramTexts, false); - if (parameters.length < 1 && editorPane.update().size() < 1) { + if (parameters.length < 1 && this.editorPane.update().size() < 1) { return; } boolean isIn = true; - List list = editorPane.update(); - List name = new ArrayList(); - for (int i = 0; i < list.size(); i++) { - name.add(list.get(i).getName()); + List list = this.editorPane.update(); + List name = new ArrayList<>(); + for (ParameterProvider parameter : list) { + name.add(parameter.getName()); } - for (int i = 0; i < parameters.length; i++) { - if (!name.contains(parameters[i].getName())) { + for (Parameter parameter : parameters) { + if (!name.contains(parameter.getName())) { isIn = false; break; } @@ -269,115 +382,175 @@ public class DBTableDataPane extends AbstractTableDataPane { if (list.size() == parameters.length && isIn) { return; } - // bug:34175 删了是否刷新对话框, 均直接刷新 - refresh(); + + refreshParameters(); } + @Override - public void populateBean(DBTableData dbtabledata) { - this.dbTableData = dbtabledata; - if (dbTableDataMenuHandler != null) { - dbTableDataMenuHandler.populate(dbtabledata); + public void populateBean(DBTableData dbTableData) { + this.dbTableData = dbTableData; + if (this.dbTableDataMenuHandler != null) { + this.dbTableDataMenuHandler.populate(dbTableData); } - ParameterProvider[] parameters = null; + Calculator c = Calculator.createCalculator(); - parameters = dbtabledata.getParameters(c); - editorPane.populate(parameters); + ParameterProvider[] parameters = dbTableData.getParameters(c); + this.editorPane.populate(parameters); - com.fr.data.impl.Connection db = null; - String query = null; - boolean isShare = false; - int maxMemeryRow = -1; - db = dbtabledata.getDatabase(); - query = dbtabledata.getQuery(); - isShare = dbtabledata.isShare(); - maxMemeryRow = dbtabledata.getMaxMemRowCount(); - this.pageQuery = dbtabledata.getPageQuerySql(); + Connection db = dbTableData.getDatabase(); + String query = dbTableData.getQuery(); + this.pageQuery = dbTableData.getPageQuerySql(); this.connectionTableProcedurePane.setSelectedDatabaseConnection(db); this.sqlTextPane.setText(query); this.sqlTextPane.requestFocus(); this.sqlTextPane.moveCaretPosition(this.sqlTextPane.getCaretPosition()); - isShareCheckBox.setSelected(isShare); - maxPanel.setValue(maxMemeryRow); + + //查找映射的配置 + this.strategyConfig = mapStrategyConfig(dbTableData); + + boolean shouldEnable = false; + ConfigFrom from = ConfigFrom.GLOBAL; + + if (this.strategyConfig != null) { + if (this.strategyConfig.enabled()) { + shouldEnable = true; + } + if (!this.strategyConfig.isUseGlobal()) { + from = ConfigFrom.INDIVIDUAL; + } + } + this.esdEnabled.setSelected(shouldEnable); + this.configFromList.setSelectedIndex(from.getIndex()); } - @Override + + protected StrategyConfig mapStrategyConfig(DBTableData dbTableData) { + //获取配置,如果是模版数据集,dbTableData实例应该是DBTableData,如果是服务器数据集是DBTableData + StrategyConfig strategyConfig = null; + if (dbTableData != null) { + //设置保存数据集的事件检查钩子 + String tplPath = HistoryTemplateListCache.getInstance().getCurrentEditingTemplate().getEditingFILE().getPath(); + + //新建模版此时不存在,不需要注册钩子 + if (dbTableData.getXmlSavedHook() == null && WorkContext.getWorkResource().exist(tplPath)) { + dbTableData.setXmlSavedHook(new DBTableDataSavedHook(tplPath, dbTableData)); + } + + //获取当前的缓存配置,没有就创建一份 + String dsName = this.dbTableData.getDsName(); + strategyConfig = DesignerStrategyConfigUtils.getStrategyConfig(dsName); + if (strategyConfig == null && dbTableData.isShare()) { + strategyConfig = StrategyConfigHelper.createStrategyConfig(true, false, true); + } + } + + return strategyConfig; + } + + public DBTableData updateBean() { + + updateDBTableData(); + + //这边只修改enable和useGlobal + boolean global = DBTableDataPane.this.configFromList.getSelectedIndex() == DBTableDataPane.ConfigFrom.GLOBAL.getIndex(); + boolean enable = DBTableDataPane.this.esdEnabled.isSelected(); + + + //未开启缓存的,如果选择了单独配置,需要创建配置 + if (this.strategyConfig == null && !global) { + this.strategyConfig = StrategyConfig.createDefault(); + } + + //设置配置来源和开启状态 + if (this.strategyConfig != null) { + this.strategyConfig.setEnable(enable); + this.strategyConfig.setUseGlobal(global); + } + + return this.dbTableData; + } + + + private void updateDBTableData() { String dbName = this.connectionTableProcedurePane.getSelectedDatabaseConnnectonName(); if (StringUtils.isBlank(dbName) || StringUtils.isBlank(this.sqlTextPane.getText())) { try { - throw new Exception(com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Basic_Connect_SQL_Cannot_Null") + "."); - } catch (Exception ignore) { - // JOptionPane.showMessageDialog(DBTableDataPane.this, - // com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Basic_Connect_SQL_Cannot_Null") + "."); + throw new Exception(Toolkit.i18nText("Fine-Design_Basic_Connect_SQL_Cannot_Null") + "."); + } catch (Exception e) { + FineLoggerFactory.getLogger().error(e.getMessage(), e); } } + + // 保存前 刷新下参数列表 保证获取到最新的参数 - refresh(); - List parameterList = editorPane.update(); - Parameter[] parameters = parameterList.toArray(new Parameter[parameterList.size()]); + refreshParameters(); + + List parameterList = this.editorPane.update(); + ParameterProvider[] parameters = parameterList.toArray(new ParameterProvider[0]); dbTableData.setDatabase(new NameDatabaseConnection(dbName)); - // p:必须先设置Parameters数组,因为setQuery里面会自动设置的 dbTableData.setParameters(parameters); - dbTableData.setQuery(this.sqlTextPane.getText()); + dbTableData.setQuery(this.sqlTextPane.getText().trim()); - dbTableData.setShare(isShareCheckBox.isSelected()); - dbTableData.setMaxMemRowCount(maxPanel.getValue()); dbTableData.setPageQuerySql(this.pageQuery); - if (dbTableDataMenuHandler != null) { - dbTableDataMenuHandler.update(); + if (this.dbTableDataMenuHandler != null) { + this.dbTableDataMenuHandler.update(); } - return dbTableData; } protected class RefreshAction extends UITableEditAction { public RefreshAction() { - this.setName(REFRESH_BUTTON); - this.setSmallIcon(BaseUtils.readIcon("/com/fr/design/images/control/refresh.png")); + setName(REFRESH_BUTTON); + setSmallIcon(IOUtils.readIcon("/com/fr/design/images/control/refresh.png")); } + @Override public void actionPerformed(ActionEvent e) { - refresh(); + DBTableDataPane.this.refreshParameters(); } + @Override public void checkEnabled() { - // do nothing } } - private class PreviewAction extends UpdateAction { + + private class PreviewAction + extends UpdateAction { public PreviewAction() { - this.setName(PREVIEW_BUTTON); - this.setMnemonic('P'); - this.setSmallIcon(BaseUtils.readIcon("/com/fr/design/images/m_file/preview.png")); + setName(PREVIEW_BUTTON); + setMnemonic('P'); + setSmallIcon(IOUtils.readIcon("/com/fr/design/images/m_file/preview.png")); } + @Override public void actionPerformed(ActionEvent evt) { - checkParameter(); + DBTableDataPane.this.checkParameter(); PreviewTablePane.previewTableData(DBTableDataPane.this.updateBean()); } } private class EditPageQueryAction extends UpdateAction { public EditPageQueryAction() { - this.setName(com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Basic_Layer_Page_Report_Page_Query")); + this.setName(Toolkit.i18nText("Fine-Design_Basic_Layer_Page_Report_Page_Query")); this.setMnemonic('L'); - this.setSmallIcon(BaseUtils.readIcon("/com/fr/design/images/m_file/text.png")); + this.setSmallIcon(IOUtils.readIcon("/com/fr/design/images/m_file/text.png")); } @Override public void actionPerformed(ActionEvent e) { - final QueryPane pane = new QueryPane(com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Basic_Layer_Page_Report_Define_Page_Query_SQL")); + final QueryPane pane = new QueryPane(Toolkit.i18nText("Fine-Design_Basic_Layer_Page_Report_Define_Page_Query_SQL")); pane.populate(pageQuery); BasicDialog dialog = pane.showWindow(DesignerContext.getDesignerFrame()); dialog.addDialogActionListener(new DialogActionAdapter() { @@ -391,9 +564,9 @@ public class DBTableDataPane extends AbstractTableDataPane { } } - private class QueryPane extends BasicPane { + private static class QueryPane extends BasicPane { private SQLEditPane pageQueryPane; - private String title; + private final String title; public QueryPane(String title) { this.title = title; @@ -410,21 +583,96 @@ public class DBTableDataPane extends AbstractTableDataPane { if (StringUtils.isBlank(text)) { return; } - pageQueryPane.setText(text); + this.pageQueryPane.setText(text); } public String update() { - String text = pageQueryPane.getText(); + String text = this.pageQueryPane.getText(); if (StringUtils.isBlank(text)) { return null; - } else { - return text; } + return text; } + @Override protected String title4PopupWindow() { - return title; + return this.title; + } + } + + + enum ConfigFrom { + GLOBAL(0, Toolkit.i18nText("Fine-Design_ESD_Use_Global_Settings")), + + INDIVIDUAL(1, Toolkit.i18nText("Fine-Design_ESD_Use_Individual_Settings")); + + int index; + + String displayText; + + ConfigFrom(int index, String displayText) { + this.index = index; + this.displayText = displayText; + } + + + static int parse(StrategyConfig config) { + return (config == null || config.isUseGlobal()) ? ConfigFrom.GLOBAL.getIndex() : ConfigFrom.INDIVIDUAL.getIndex(); + } + + public int getIndex() { + return this.index; + } + + + public String getDisplayText() { + return this.displayText; + } + + + public String toString() { + return getDisplayText(); + } + } + + + private static class DBTableDataSavedHook implements XMLSavedHook { + + private static final long serialVersionUID = 4925391747683335372L; + + private final String tplPath; + private String origName; + + private String origConnection; + + private String origQuery; + + public DBTableDataSavedHook(String tplPath, DBTableData origDBTableData) { + this.tplPath = tplPath; + this.origName = origDBTableData.getDsName(); + this.origConnection = origDBTableData.getDatabase().toString(); + this.origQuery = origDBTableData.getQuery(); + } + + @Override + public void doAfterSaved(DBTableData saved) { + String dsName = saved.getDsName(); + String conn = saved.getDatabase().toString(); + String query = saved.getQuery(); + + + //检查数据集名称、数据链接和sql是否修改,如果修改需要触发缓存监听事件 + if (!dsName.equals(origName) || !conn.equals(origConnection) || !query.equals(origQuery)) { + if (StringUtils.isNotEmpty(tplPath) && StringUtils.isNotEmpty(origName)) { + //新建数据集的origName为null,不用触发 + StrategyEventsNotifier.modifyDataSet(new DSMapping(tplPath, new DsNameTarget(origName))); + } + } + + this.origName = dsName; + this.origConnection = conn; + this.origQuery = query; } } } diff --git a/designer-base/src/main/java/com/fr/design/gui/itree/filetree/FileTreeIcon.java b/designer-base/src/main/java/com/fr/design/gui/itree/filetree/FileTreeIcon.java index b418c1db4..e0515fe81 100644 --- a/designer-base/src/main/java/com/fr/design/gui/itree/filetree/FileTreeIcon.java +++ b/designer-base/src/main/java/com/fr/design/gui/itree/filetree/FileTreeIcon.java @@ -48,6 +48,9 @@ public class FileTreeIcon { public static final Icon MODERN_CHT_FILE_IMAGE_ICON = BaseUtils.readIcon("/com/fr/design/images/gui/modern_style_cht_file_icon_16x16.png"); + public static final Icon CPTX_ICON = BaseUtils.readIcon("/com/fr/nx/app/designer/cptx_file_icon.png"); + public static final Icon CPTX_LOCKED_ICON = BaseUtils.readIcon("/com/fr/nx/app/designer/cptx_file_icon_locked.png"); + public static final LockIcon FOLDER_LOCK_ICON = new LockIcon(BaseUtils.readImage("/com/fr/design/images/gui/fold.png")); public static final LockIcon FILE_LOCK_ICON = @@ -108,6 +111,7 @@ public class FileTreeIcon { public final static int CPT_FILE = 11; //.cpt public final static int FRM_FILE = 12; //.form .frm public final static int CHT_FILE = 13; //.chart .cht + public final static int CPTX_FILE = 14; //.cptx public static Icon getIcon(File file) { return FileTreeIcon.getIcon(file, false); @@ -252,6 +256,12 @@ public class FileTreeIcon { } else { return FileTreeIcon.MODERN_CPT_FILE_IMAGE_ICON; } + } else if (fileType == CPTX_FILE) { + if (isLocked) { + return FileTreeIcon.CPTX_LOCKED_ICON; + } else { + return FileTreeIcon.CPTX_ICON; + } } else if (fileType == FRM_FILE) { //form frm if (isLocked) { return FileTreeIcon.FRM_FILE_LOCK_ICON; @@ -314,6 +324,8 @@ public class FileTreeIcon { return BMP_FILE; } else if (fileName.endsWith(".cpt")) { return CPT_FILE; + } else if (fileName.endsWith(".cptx")) { + return CPTX_FILE; } else if (fileName.endsWith(".frm") || fileName.endsWith(".form")) { return FRM_FILE; } else if (fileName.endsWith(".cht") || fileName.endsWith(".chart")) { 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 483e74d9d..f558f93fc 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 @@ -3,6 +3,7 @@ package com.fr.design.mainframe; import com.fr.base.BaseUtils; import com.fr.base.FRContext; import com.fr.base.Parameter; +import com.fr.base.extension.FileExtension; import com.fr.base.io.BaseBook; import com.fr.base.iofile.attr.DesignBanCopyAttrMark; import com.fr.base.iofile.attr.TemplateIdAttrMark; @@ -14,6 +15,7 @@ import com.fr.design.ExtraDesignClassManager; import com.fr.design.actions.TableDataSourceAction; import com.fr.design.actions.edit.RedoAction; import com.fr.design.actions.edit.UndoAction; +import com.fr.design.actions.file.BatchCompileAction; import com.fr.design.actions.file.SaveAsTemplateAction; import com.fr.design.actions.file.SaveTemplateAction; import com.fr.design.actions.file.WebPreviewUtils; @@ -60,6 +62,8 @@ import com.fr.general.ComparatorUtils; import com.fr.log.FineLoggerFactory; import com.fr.plugin.context.PluginContext; import com.fr.plugin.context.PluginRuntime; +import com.fr.nx.app.designer.toolbar.CompileAction; +import com.fr.nx.app.designer.toolbar.TemplateTransformer; import com.fr.plugin.injectable.PluginModule; import com.fr.plugin.manage.PluginFilter; import com.fr.plugin.observer.PluginEvent; @@ -781,10 +785,12 @@ public abstract class JTemplate> protected boolean saveToNewFile(String oldName) { boolean result = false; + String path = this.editingFILE.getPath(); Set providers = ExtraDesignClassManager.getInstance().getArray(ReportSupportedFileUIProvider.XML_TAG); for (ReportSupportedFileUIProvider provider : providers) { - result = result || provider.saveToNewFile(this.editingFILE.getPath(), this); + result = result || provider.saveToNewFile(path, this); } + result = result || saveToNewFile4Cptx(path); if (!result) { result = result || this.saveFile(); //更换最近打开 @@ -794,6 +800,14 @@ public abstract class JTemplate> return result; } + private boolean saveToNewFile4Cptx(String targetPath) { + if (FileExtension.CPTX.matchExtension(targetPath)) { + TemplateTransformer.TO_CPTX.transform(this); + return true; + } + return false; + } + protected void mkNewFile(FILE file) { try { file.mkfile(); @@ -887,7 +901,7 @@ public abstract class JTemplate> } else if (DesignerMode.isAuthorityEditing()) { return new ShortCut[]{new SaveTemplateAction(this), new UndoAction(this), new RedoAction(this)}; } else { - return new ShortCut[]{new SaveTemplateAction(this), new SaveAsTemplateAction(this), new UndoAction(this), new RedoAction(this)}; + return new ShortCut[]{new SaveTemplateAction(this), new SaveAsTemplateAction(this), new BatchCompileAction(), new UndoAction(this), new RedoAction(this)}; } } @@ -1323,8 +1337,10 @@ public abstract class JTemplate> * @return 按钮组 */ public UIButton[] createExtraButtons() { + UIButton[] uiButtons = new UIButton[] { + (UIButton) new CompileAction().createToolBarComponent() + }; Set providers = ExtraDesignClassManager.getInstance().getArray(DesignerFrameUpButtonProvider.XML_TAG); - UIButton[] uiButtons = new UIButton[0]; for (DesignerFrameUpButtonProvider provider : providers) { uiButtons = ArrayUtils.addAll(uiButtons, provider.getUpButtons(getMenuState())); } diff --git a/designer-base/src/main/java/com/fr/design/mainframe/vcs/common/VcsHelper.java b/designer-base/src/main/java/com/fr/design/mainframe/vcs/common/VcsHelper.java index 8229c9f8d..0ec5b9c4f 100644 --- a/designer-base/src/main/java/com/fr/design/mainframe/vcs/common/VcsHelper.java +++ b/designer-base/src/main/java/com/fr/design/mainframe/vcs/common/VcsHelper.java @@ -1,5 +1,6 @@ package com.fr.design.mainframe.vcs.common; + import com.fr.concurrent.NamedThreadFactory; import com.fr.design.DesignerEnvManager; import com.fr.design.file.HistoryTemplateListCache; diff --git a/designer-base/src/main/java/com/fr/design/preview/PagePlusPreview.java b/designer-base/src/main/java/com/fr/design/preview/PagePlusPreview.java new file mode 100644 index 000000000..04fd6623c --- /dev/null +++ b/designer-base/src/main/java/com/fr/design/preview/PagePlusPreview.java @@ -0,0 +1,41 @@ +package com.fr.design.preview; + +import com.fr.design.fun.impl.AbstractPreviewProvider; +import com.fr.general.web.ParameterConstants; +import com.fr.locale.InterProviderFactory; + +import java.util.HashMap; +import java.util.Map; + +import static com.fr.nx.app.web.v9.PagePlusActor.TYPE; + +public class PagePlusPreview extends AbstractPreviewProvider { + private static final int CODE = 100; + + @Override + public String nameForPopupItem() { + return InterProviderFactory.getProvider().getLocText("Fine-Plugin_Engine-Page-Plus"); + } + + @Override + public String iconPathForPopupItem() { + return "com/fr/design/images/buttonicon/pages.png"; + } + + @Override + public String iconPathForLarge() { + return "com/fr/design/images/buttonicon/pageb24.png"; + } + + @Override + public int previewTypeCode() { + return CODE; + } + + @Override + public Map parametersForPreview() { + Map map = new HashMap(); + map.put(ParameterConstants.OP, TYPE); + return map; + } +} diff --git a/designer-base/src/main/java/com/fr/design/preview/WriteEnhancePreview.java b/designer-base/src/main/java/com/fr/design/preview/WriteEnhancePreview.java new file mode 100644 index 000000000..96e90d5a2 --- /dev/null +++ b/designer-base/src/main/java/com/fr/design/preview/WriteEnhancePreview.java @@ -0,0 +1,40 @@ +package com.fr.design.preview; + +import com.fr.base.io.IOFile; +import com.fr.design.fun.impl.AbstractPreviewProvider; + + +import java.util.HashMap; +import java.util.Map; + +/** + * Created by loy on 2017/7/7. + */ +public class WriteEnhancePreview extends AbstractPreviewProvider { + @Override + public String nameForPopupItem() { + return com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Basic_Write_Enhance_Preview"); + } + + @Override + public String iconPathForPopupItem() { + return "com/fr/design/images/buttonicon/writes.png"; + } + + @Override + public String iconPathForLarge() { + return "com/fr/design/images/buttonicon/writeb24.png"; + } + + @Override + public int previewTypeCode() { + return IOFile.WRITE_ENHANCE_PREVIEW_TYPE; + } + + @Override + public Map parametersForPreview() { + Map map = new HashMap(); + map.put("op", "write_plus"); + return map; + } +} 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 43c3f43ad..a97b7db9d 100644 --- a/designer-base/src/main/java/com/fr/file/FILEChooserPane.java +++ b/designer-base/src/main/java/com/fr/file/FILEChooserPane.java @@ -770,6 +770,7 @@ public class FILEChooserPane extends BasicPane { // ben:filefilter设置初值为cpt过滤 this.addChooseFILEFilter(new ChooseFileFilter(FileExtension.CPT, appName + Toolkit.i18nText("Fine-Design_Report_Template_File"))); + this.addChooseFILEFilter(new ChooseFileFilter(FileExtension.CPTX, appName + Toolkit.i18nText("Fine-Design_Report_Template_File"))); // richer:form文件 daniel 改成三个字 this.addChooseFILEFilter(new ChooseFileFilter(FileExtension.FRM, appName + Toolkit.i18nText("Fine-Design_Report_Template_File"))); diff --git a/designer-base/src/main/java/com/fr/nx/app/designer/cptx/io/DesignReadWritableProvider.java b/designer-base/src/main/java/com/fr/nx/app/designer/cptx/io/DesignReadWritableProvider.java new file mode 100644 index 000000000..0896e66e9 --- /dev/null +++ b/designer-base/src/main/java/com/fr/nx/app/designer/cptx/io/DesignReadWritableProvider.java @@ -0,0 +1,60 @@ +package com.fr.nx.app.designer.cptx.io; + +import com.fr.common.annotations.Negative; +import com.fr.file.FILE; +import com.fr.nx.app.designer.utils.DesignerCptxFileUtils; +import com.fr.nx.cptx.io.handle.impl.AbstractCptxIOProvider; +import com.fr.nx.cptx.pack.util.CompiledReportInputStream; +import com.fr.nx.cptx.pack.util.CompiledReportOutputStream; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +/** + * 用于设计器保存编译后的结果并缓存可web预览模版 + * 没有可观的内存实现,内存实现方式复杂,赶发布,先提供简单的内存实现方式 + * + * @author: Maksim + * @Date: Created in 2020/4/10 + * @Description: 用于cptx写操作的Provider + */ +@Negative(until = "2020-05-10") +public class DesignReadWritableProvider extends AbstractCptxIOProvider { + /** + * 原文件 + */ + private final FILE file; + /** + * 编译文件夹 + */ + private final String compileDir; + + private CompiledReportOutputStream outputStream; + + public DesignReadWritableProvider(FILE file) { + this.file = file; + this.compileDir = DesignerCptxFileUtils.generateCompileDir(file); + } + + @Override + public InputStream open() throws Exception { + if (outputStream == null) { + throw new IOException("can not read cptx template before compile"); + } + return new CompiledReportInputStream(outputStream.toByteArray()); + } + + /** + * 对于写操作,用到的只有这个方法,以流形式来操作 + * + * @return 保存的目标输出流 + * @throws Exception e + */ + @Override + public OutputStream createTemp() throws Exception { + outputStream = new CompiledReportOutputStream(file.asOutputStream(), compileDir); + return outputStream; + } + +} diff --git a/designer-base/src/main/java/com/fr/nx/app/designer/monitor/DesignerMetricRecorder.java b/designer-base/src/main/java/com/fr/nx/app/designer/monitor/DesignerMetricRecorder.java new file mode 100644 index 000000000..0644bd511 --- /dev/null +++ b/designer-base/src/main/java/com/fr/nx/app/designer/monitor/DesignerMetricRecorder.java @@ -0,0 +1,97 @@ +package com.fr.nx.app.designer.monitor; + +import com.fr.design.mainframe.errorinfo.ErrorInfo; +import com.fr.intelli.record.MetricRegistry; +import com.fr.json.JSON; +import com.fr.json.JSONFactory; +import com.fr.json.JSONObject; +import com.fr.message.ErrorMessage; +import com.fr.nx.app.designer.toolbar.TransformResult; + +/** + * Created by loy on 2021/1/18. + * + *

设计器埋点记录相关 + */ +public class DesignerMetricRecorder { + + private static final String ATTR_TEMPLATE_ID = "templateid"; + private static final String ATTR_USERNAME = "username"; + private static final String ATTR_UUID = "uuid"; + private static final String ATTR_ACTIVE_KEY = "activekey"; + private static final String ATTR_TEST_LOG = "testlog"; + private static final String ATTR_REPORT_CONTENT = "reportcontent"; + private static final String ATTR_ERROR_STACK = "errorstack"; + private static final String IDENTIFICATION = "view_plus"; + + private DesignerMetricRecorder() { + } + + /** + * 模板转换失败的埋点 + */ + public static void submitFailedTransform(TransformResult result, String templateID, String name, Exception e) { + if (isPressureTesting()) { + return; + } + saveAsJSON(result, templateID, name, e); + } + + /** + * 记录在设计器的错误信息收集中 + */ + private static void saveToErrorInfo(TransformResult result, String templateID, String name, Exception e, String unsupportMessage) { + JSONObject jo = JSONFactory.createJSON(JSON.OBJECT); + jo.put(ATTR_USERNAME, IDENTIFICATION); + jo.put(ATTR_UUID, IDENTIFICATION); + jo.put(ATTR_ACTIVE_KEY, IDENTIFICATION); + jo.put(ATTR_TEMPLATE_ID, templateID); + jo.put(ATTR_REPORT_CONTENT, name); + if (result == TransformResult.FAILED) { + jo.put(ATTR_TEST_LOG, e); + jo.put(ATTR_ERROR_STACK, e.getMessage()); + } else { + jo.put(ATTR_TEST_LOG, unsupportMessage); + } + new ErrorInfo().saveFileToCache(jo); + } + + /** + * 记录在fine_record_error表中 + */ + private static void saveToErrorMessage(TransformResult result, String templateID, String name, Exception e, String unsupportMessage) { + String errorMessage = unsupportMessage; + if (result == TransformResult.FAILED) { + errorMessage = e.getStackTrace()[0].toString(); + } + ErrorMessage message = ErrorMessage.build(e == null ? errorMessage : e.getMessage(), errorMessage); + message.setTname(name); + message.setDisplayName(name); + MetricRegistry.getMetric().submit(message); + } + + private static void saveAsJSON(TransformResult result, String templateID, String name, Exception e) { + saveToErrorInfo(result, templateID, name, e, null); + saveToErrorMessage(result, templateID, name, e, null); + } + + /** + * 预编译中不支持的功能的埋点 + */ + public static void submitUnSupportTransform(TransformResult result, String templateID, String name, String unsupportMessage) { + if (isPressureTesting()) { + return; + } + saveToErrorInfo(result, templateID, name, null, unsupportMessage); + saveToErrorMessage(result, templateID, name, null, unsupportMessage); + } + + /** + * 埋点对压测性能的影响控制参数 + * + * @return 是否压测 + */ + public static boolean isPressureTesting() { + return Boolean.parseBoolean(System.getProperty("monitorDebug")); + } +} diff --git a/designer-base/src/main/java/com/fr/nx/app/designer/toolbar/CompileAction.java b/designer-base/src/main/java/com/fr/nx/app/designer/toolbar/CompileAction.java new file mode 100644 index 000000000..4496ecd9a --- /dev/null +++ b/designer-base/src/main/java/com/fr/nx/app/designer/toolbar/CompileAction.java @@ -0,0 +1,116 @@ +package com.fr.nx.app.designer.toolbar; + +import com.fr.base.extension.FileExtension; +import com.fr.design.actions.UpdateAction; +import com.fr.design.file.HistoryTemplateListCache; +import com.fr.design.gui.ibutton.UIButton; +import com.fr.design.mainframe.DesignerContext; +import com.fr.design.mainframe.JTemplate; +import com.fr.design.menu.MenuKeySet; +import com.fr.file.FILE; +import com.fr.file.FileNodeFILE; +import com.fr.general.IOUtils; +import com.fr.locale.InterProviderFactory; +import com.fr.nx.app.designer.utils.CompileTransformUtil; + +import javax.swing.Icon; +import javax.swing.JComponent; +import javax.swing.KeyStroke; +import java.awt.event.ActionEvent; +import java.awt.event.KeyEvent; + +import static com.fr.design.dialog.FineJOptionPane.showConfirmDialog; +import static com.fr.design.gui.syntax.ui.rtextarea.RTADefaultInputMap.DEFAULT_MODIFIER; +import static javax.swing.JOptionPane.OK_CANCEL_OPTION; +import static javax.swing.JOptionPane.OK_OPTION; +import static javax.swing.JOptionPane.WARNING_MESSAGE; + +/** + * Created by kerry on 2019-10-14 + */ +public class CompileAction extends UpdateAction { + public static final Icon TRANS_ICON = IOUtils.readIcon("/com/fr/nx/app/designer/transform.png"); + + private static final MenuKeySet COMPILE_ATTR = new MenuKeySet() { + @Override + public char getMnemonic() { + return 'C'; + } + + @Override + public String getMenuName() { + return InterProviderFactory.getProvider().getLocText("Fine-Plugin_Engine-Transform-Tooltip"); + } + + @Override + public KeyStroke getKeyStroke() { + return KeyStroke.getKeyStroke(KeyEvent.VK_T, DEFAULT_MODIFIER); + } + }; + + public CompileAction() { + initMenuStyle(); + } + + private void initMenuStyle() { + this.setMenuKeySet(COMPILE_ATTR); + this.setName(getMenuKeySet().getMenuKeySetName()); + this.setMnemonic(getMenuKeySet().getMnemonic()); + this.setSmallIcon(TRANS_ICON); + this.setAccelerator(getMenuKeySet().getKeyStroke()); + } + + @Override + public void actionPerformed(ActionEvent e) { + JTemplate jtemplate = DesignerContext.getDesignerFrame().getSelectedJTemplate(); + if (jtemplate == null || jtemplate.getEditingFILE() == null) { + return; + } + FILE currentTemplate = jtemplate.getEditingFILE(); + if(currentTemplate instanceof FileNodeFILE){ + transformAndDisplay(jtemplate); + }else { + //模板不在报表环境下,提示保存 + int selVal = showConfirmDialog( + DesignerContext.getDesignerFrame(), + InterProviderFactory.getProvider().getLocText("Fine-Plugin-Engine_Preview_Message"), + InterProviderFactory.getProvider().getLocText("Fine-Plugin-Engine_Transformer_Tips"), + OK_CANCEL_OPTION, + WARNING_MESSAGE); + + if (OK_OPTION == selVal) { + //保存成功才会执行编译 + if (jtemplate.saveAsTemplate2Env()) { + transformAndDisplay(jtemplate); + } + } + } + + } + + //编译模板并展示 + private void transformAndDisplay(JTemplate jtemplate){ + String path = jtemplate.getEditingFILE().getPath(); + TemplateTransformer transformer = TemplateTransformer.parse(path); + FILE targetFile = CompileTransformUtil.getTargetFile(jtemplate, transformer); + TransformResult result = transformer.transform(jtemplate, targetFile); + jtemplate.fireJTemplateSaved(); + result.display(); + } + + @Override + public boolean isEnabled() { + JTemplate jt = HistoryTemplateListCache.getInstance().getCurrentEditingTemplate(); + String path = jt.getEditingFILE().getPath(); + return FileExtension.CPTX.matchExtension(path) || FileExtension.CPT.matchExtension(path); + } + + @Override + public JComponent createToolBarComponent() { + UIButton transBtn = (UIButton) super.createToolBarComponent(); + transBtn.set4ToolbarButton(); + transBtn.setToolTipText(InterProviderFactory.getProvider().getLocText("Fine-Plugin_Engine-Transform-Tooltip")); + return transBtn; + } + +} diff --git a/designer-base/src/main/java/com/fr/nx/app/designer/toolbar/TemplateTransformer.java b/designer-base/src/main/java/com/fr/nx/app/designer/toolbar/TemplateTransformer.java new file mode 100644 index 000000000..6294fa4d8 --- /dev/null +++ b/designer-base/src/main/java/com/fr/nx/app/designer/toolbar/TemplateTransformer.java @@ -0,0 +1,221 @@ +package com.fr.nx.app.designer.toolbar; + +import com.fr.base.extension.FileExtension; +import com.fr.design.file.HistoryTemplateListCache; +import com.fr.design.file.MutilTempalteTabPane; +import com.fr.design.mainframe.DesignerContext; +import com.fr.design.mainframe.JTemplate; +import com.fr.file.FILE; +import com.fr.file.FileNodeFILE; +import com.fr.file.filetree.FileNode; +import com.fr.general.ComparatorUtils; +import com.fr.log.FineLoggerFactory; +import com.fr.main.impl.WorkBook; +import com.fr.nx.app.designer.monitor.DesignerMetricRecorder; +import com.fr.nx.compile.CompileStatus; +import com.fr.nx.compile.ReportCompiler; +import com.fr.nx.compile.adapter.LegacyWorkBookAdapter; +import com.fr.nx.compile.util.ReportCompileUtils; +import com.fr.nx.cptx.CptxIOManager; +import com.fr.nx.cptx.cache.CptxTemplatePool; +import com.fr.nx.cptx.entry.CptxTemplate; +import com.fr.nx.cptx.io.handle.CptxTemplateHandle; +import com.fr.nx.app.designer.cptx.io.DesignReadWritableProvider; +import com.fr.nx.cptx.monitor.CompileMonitorHandler; +import com.fr.nx.cptx.utils.CptxFileUtils; +import com.fr.nx.data.layer.LayerItem; +import com.fr.nx.data.layer.LayerProps; +import com.fr.nx.template.compile.CompiledReport; +import com.fr.stable.StringUtils; +import org.jetbrains.annotations.NotNull; + +import java.io.OutputStream; + +import static com.fr.base.extension.FileExtension.ALL; +import static com.fr.base.extension.FileExtension.CPT; +import static com.fr.base.extension.FileExtension.CPTX; + +/** + * 模板转换器, 可以把cptx模板转为cpt, 或者cpt模板转为cptx. + * 以后可以扩展支持frm frmx互相转换 + */ + +public enum TemplateTransformer { + + TO_CPTX(CPT) { + @Override + public TransformResult transform(JTemplate jtemplate, FILE file) { + WorkBook workbook = (WorkBook) jtemplate.getTarget(); + TransformResultInfo resultInfo = compileCPTX(workbook, file); + if (needDoAfterTransformed(resultInfo.getResult())) { + doAfterTransformed(file, jtemplate); + } + return resultInfo.getResult(); + } + }, + TO_CPT(CPTX) { + @Override + public TransformResult transform(JTemplate jtemplate, FILE file) { + WorkBook workbook = (WorkBook) jtemplate.getTarget(); + try { + workbook.export(file.asOutputStream()); + doAfterTransformed(file, jtemplate); + return TransformResult.SUCCESS; + } catch (Exception e) { + FineLoggerFactory.getLogger().error(e.getMessage(), e); + return TransformResult.FAILED; + } + } + }, + OTHER(ALL); + private FileExtension extension; + + TemplateTransformer(FileExtension extension) { + this.extension = extension; + } + + public TransformResult transform(JTemplate jtemplate) { + return transform(jtemplate, jtemplate.getEditingFILE()); + } + + public TransformResult transform(JTemplate jtemplate, FILE newFile) { + return TransformResult.UNSUPPORT; + } + + public static TemplateTransformer parse(String path) { + for (TemplateTransformer transformer : values()) { + if (transformer.extension.matchExtension(path)) { + return transformer; + } + } + return OTHER; + } + + private static boolean needDoAfterTransformed(TransformResult result) { + return ComparatorUtils.equals(TransformResult.SUCCESS, result) + || ComparatorUtils.equals(TransformResult.UNSUPPORT, result); + } + + private static void doAfterTransformed(FILE file, JTemplate jtemplate) { + JTemplate jt = HistoryTemplateListCache.getInstance().getCurrentEditingTemplate(); + if (ComparatorUtils.equals(file.getPath(), jt.getPath())) { + HistoryTemplateListCache.getInstance().closeSelectedReport(jt); + DesignerContext.getDesignerFrame().openTemplate(file); + return; + } + MutilTempalteTabPane.getInstance().setIsCloseCurrent(true); + MutilTempalteTabPane.getInstance().closeFormat(jt); + MutilTempalteTabPane.getInstance().closeSpecifiedTemplate(jt); + DesignerContext.getDesignerFrame().openTemplate(file); + } + + + public static FILE createOutputFile(String oldPath, String oldSuffix, String newSuffix) { + String newPath = generateNewPath(oldPath, oldSuffix, newSuffix); + return new FileNodeFILE(new FileNode(newPath, false)); + } + + private static String generateNewPath(String oldPath, String oldSuffix, String newSuffix) { + if (StringUtils.isEmpty(oldPath) || StringUtils.isEmpty(oldSuffix) || StringUtils.isEmpty(newSuffix)) { + return oldPath; + } + + if (oldPath.endsWith(oldSuffix)) { + return StringUtils.cutStringEndWith(oldPath, oldSuffix) + newSuffix; + } + return oldPath; + } + + /** + * 编译和保存 + * + * @param workbook work + * @param file file + * @return 编译保存结果 + */ + @NotNull + public static TransformResultInfo compileCPTX(WorkBook workbook, FILE file) { + //对于非工作目录的cptx,修改无需执行编译 + if(!(file instanceof FileNodeFILE)){ + try { + OutputStream outputStream = file.asOutputStream(); + workbook.export(outputStream); + return TransformResultInfo.generateResult(TransformResult.SUCCESS).saved(true); + }catch (Exception e){ + FineLoggerFactory.getLogger().error(e.getMessage(), e); + return TransformResultInfo.generateResult(TransformResult.FAILED).saved(false); + } + } + boolean saved; + CompiledReport report = null; + CompileStatus compileStatus = CompileStatus.SUCCEED; + if (workbook == null || file == null) { + return TransformResultInfo + .generateResult(TransformResult.FAILED, "work and file must not be null") + .saved(false); + } + String failMessage = null; + // 编译 + ReportCompiler compiler; + try { + compiler = new ReportCompiler(new LegacyWorkBookAdapter(workbook)); + long startTime = System.currentTimeMillis(); + compiler.compile(); + report = compiler.getCompiledReport(); + // 折叠树埋点 + LayerProps props = ReportCompileUtils.getLayerProps(report); + LayerItem[] items; + if (props != null && (items = props.getLayerItems()) != null) { + CompileMonitorHandler.submitTreeCompileFocusPoint( + startTime, file.getPath(), items.length, + props.getExpandLayer(), true + ); + } + compileStatus = compiler.getStatus(); + failMessage = compiler.getFailMessage(); + long endTime = System.currentTimeMillis(); + CompileMonitorHandler.submitCompileMessage(startTime, endTime, file.getPath(), ""); + if (compileStatus != CompileStatus.SUCCEED) { + DesignerMetricRecorder.submitUnSupportTransform( + TransformResult.UNSUPPORT, + workbook.getTemplateID(), + file.getName(), + compileStatus == CompileStatus.FAILED_UNSUPPORT ? failMessage : null + ); + } + } catch (Exception exception) { + String templateID = workbook.getTemplateID(); + String fileName = file.getName(); + DesignerMetricRecorder.submitFailedTransform(TransformResult.FAILED, templateID, fileName, exception); + FineLoggerFactory.getLogger().error(exception.getMessage(), exception); + } + + // 构建编译结果,当前的 cptx template 是未经反序列化钩子处理过的 cptx 模版对象,不能直接用于模版预览 + CptxTemplate template = CptxIOManager.createCptxTemplate(workbook, report, compileStatus, failMessage); + // 保存 + DesignReadWritableProvider cptx = new DesignReadWritableProvider(file); + CptxTemplateHandle handle = CptxIOManager.create(template, cptx); + try { + saved = handle.save(); + } catch (Exception exception) { + // 存储cptx格式报错或者失败 + FineLoggerFactory.getLogger().error(exception.getMessage(), exception); + DesignerMetricRecorder.submitFailedTransform(TransformResult.FAILED, workbook.getTemplateID(), file.getName(), exception); + return TransformResultInfo.generateResult(TransformResult.FAILED, failMessage).saved(false); + } + // 读取可 web 预览模版并缓存 + try { + // todo 是否考虑异步 + String timestampedPath = CptxFileUtils.getTimestampedPath(file.getPath()); + CptxTemplate previewCptxTemplate = CptxIOManager.open(cptx).getTemplate(); + CptxTemplatePool.getInstance().addCptxTemplate(timestampedPath, previewCptxTemplate); + } catch (Exception e) { + FineLoggerFactory.getLogger().error(e.getMessage(), e); + } + if (report == null) { + // 编译报错或者失败 + return TransformResultInfo.generateResult(TransformResult.FAILED, failMessage).saved(saved); + } + return TransformResultInfo.generateResult(TransformResult.parse(compileStatus), failMessage).saved(saved); + } +} diff --git a/designer-base/src/main/java/com/fr/nx/app/designer/toolbar/TransformResult.java b/designer-base/src/main/java/com/fr/nx/app/designer/toolbar/TransformResult.java new file mode 100644 index 000000000..9668ec805 --- /dev/null +++ b/designer-base/src/main/java/com/fr/nx/app/designer/toolbar/TransformResult.java @@ -0,0 +1,55 @@ +package com.fr.nx.app.designer.toolbar; + +import com.fr.design.file.TemplateTreePane; +import com.fr.design.mainframe.DesignerContext; +import com.fr.locale.InterProviderFactory; +import com.fr.nx.compile.CompileStatus; + +import javax.swing.JOptionPane; + +public enum TransformResult { + + SUCCESS(InterProviderFactory.getProvider().getLocText("Fine-Plugin_Engine-Transform-Success")) { + @Override + public void display() { + // 转换成功后, 刷新下目录树 + TemplateTreePane.getInstance().refresh(); + super.display(); + } + }, + FAILED(InterProviderFactory.getProvider().getLocText("Fine-Plugin_Engine-Transform-Failed")), + UNSUPPORT(InterProviderFactory.getProvider().getLocText("Fine-Plugin_Engine-Transform-Unsupport")){ + @Override + public void display() { + JOptionPane.showMessageDialog(DesignerContext.getDesignerFrame(), TransformResult.SUCCESS.toString()); + } + }; + + private String msg; + + private TransformResult(String msg) { + this.msg = msg; + } + + // 展示结果 + public void display() { + JOptionPane.showMessageDialog(DesignerContext.getDesignerFrame(), this.toString()); + } + + @Override + public String toString() { + return this.msg; + } + + public static TransformResult parse(CompileStatus status) { + switch (status) { + case SUCCEED: + return TransformResult.SUCCESS; + case FAILED_UNSUPPORT: + return TransformResult.UNSUPPORT; + default: + return TransformResult.FAILED; + } + + } +} diff --git a/designer-base/src/main/java/com/fr/nx/app/designer/toolbar/TransformResultInfo.java b/designer-base/src/main/java/com/fr/nx/app/designer/toolbar/TransformResultInfo.java new file mode 100644 index 000000000..e988e04e7 --- /dev/null +++ b/designer-base/src/main/java/com/fr/nx/app/designer/toolbar/TransformResultInfo.java @@ -0,0 +1,64 @@ +package com.fr.nx.app.designer.toolbar; + +import com.fr.locale.InterProviderFactory; +import com.fr.stable.StringUtils; + +/** + * Created by kerry on 2020-01-15 + */ +public class TransformResultInfo { + + private boolean saved; + private TransformResult result; + private final String transformLog; + + public static TransformResultInfo generateResult(TransformResult result, String transformLog) { + return new TransformResultInfo(result, transformLog); + } + + public static TransformResultInfo generateResult(TransformResult result) { + return new TransformResultInfo(result, StringUtils.EMPTY); + } + + + private TransformResultInfo(TransformResult result, String transformLog) { + this.result = result; + this.transformLog = transformLog; + this.saved = false; + } + + public boolean isSaved() { + return saved; + } + + public void setSaved(boolean saved) { + this.saved = saved; + } + + public TransformResultInfo saved(boolean saved) { + setSaved(saved); + return this; + } + + public TransformResult getResult() { + return result; + } + + public void setResult(TransformResult result) { + this.result = result; + } + + public String getTransformLog() { + switch (this.result) { + case FAILED: + return transformLog + "\n" + + InterProviderFactory.getProvider().getLocText("Fine-Plugin_Engine_Transform_Failed_Tip"); + case SUCCESS: + return InterProviderFactory.getProvider().getLocText("Fine-Plugin_Engine_Transform_Success_Tip"); + case UNSUPPORT: + return transformLog + "\n" + + InterProviderFactory.getProvider().getLocText("Fine-Plugin_Engine_Transform_Unsupport_Tip"); + } + return transformLog; + } +} diff --git a/designer-base/src/main/java/com/fr/nx/app/designer/transform/BatchTransformProgress.java b/designer-base/src/main/java/com/fr/nx/app/designer/transform/BatchTransformProgress.java new file mode 100644 index 000000000..7b11cd34c --- /dev/null +++ b/designer-base/src/main/java/com/fr/nx/app/designer/transform/BatchTransformProgress.java @@ -0,0 +1,25 @@ +package com.fr.nx.app.designer.transform; + +/** + * Created by kerry on 2019-12-10 + */ +public class BatchTransformProgress { + private double progress = 0.0D; + private int total = 0; + private int complete = 0; + + public BatchTransformProgress(int total) { + this.total = total; + } + + + public double getProgress() { + return this.progress; + } + + + public void updateProgress() { + this.complete++; + this.progress = this.complete * 1D / this.total; + } +} diff --git a/designer-base/src/main/java/com/fr/nx/app/designer/transform/BatchTransformUtil.java b/designer-base/src/main/java/com/fr/nx/app/designer/transform/BatchTransformUtil.java new file mode 100644 index 000000000..aef4eed21 --- /dev/null +++ b/designer-base/src/main/java/com/fr/nx/app/designer/transform/BatchTransformUtil.java @@ -0,0 +1,25 @@ +package com.fr.nx.app.designer.transform; + +import com.fr.file.filetree.FileNode; + +import java.util.ArrayList; +import java.util.List; + +/** + * Created by kerry on 2020-02-14 + */ +public class BatchTransformUtil { + private BatchTransformUtil() { + + } + + public static FileNode[] filterTransformedFile(FileNode[] fileNodes, List transformedList){ + List list = new ArrayList(); + for (FileNode fileNode : fileNodes) { + if (!transformedList.contains(fileNode)) { + list.add(fileNode); + } + } + return list.toArray(new FileNode[list.size()]); + } +} diff --git a/designer-base/src/main/java/com/fr/nx/app/designer/transform/BatchTransformer.java b/designer-base/src/main/java/com/fr/nx/app/designer/transform/BatchTransformer.java new file mode 100644 index 000000000..0f6a76f83 --- /dev/null +++ b/designer-base/src/main/java/com/fr/nx/app/designer/transform/BatchTransformer.java @@ -0,0 +1,71 @@ +package com.fr.nx.app.designer.transform; + +import com.fr.design.utils.concurrent.ThreadFactoryBuilder; +import com.fr.file.filetree.FileNode; +import com.fr.plugin.context.PluginContexts; +import com.fr.nx.app.designer.toolbar.TransformResultInfo; +import com.fr.nx.app.designer.utils.CompileTransformUtil; + +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ExecutorService; + +/** + * Created by kerry on 2019-12-10 + */ +public class BatchTransformer { + private BatchTransformProgress progress = new BatchTransformProgress(0); + private Map transformResults = new ConcurrentHashMap(); + private UpdateCallBack updateCallBack; + private static final int MAXPOOLSIZE = 5; + private static final String THREAD_NAME_TEMPLATE = "batchtransform-thread-%s"; + private ExecutorService threadPoolExecutor = + PluginContexts.currentContext().newFixedThreadPool(MAXPOOLSIZE, + new ThreadFactoryBuilder().setNameFormat(THREAD_NAME_TEMPLATE).build()); + + public BatchTransformer(UpdateCallBack updateCallBack) { + this.updateCallBack = updateCallBack; + } + + + public void batchTransform(List fileNodes) { + //先清理下 + transformResults.clear(); + progress = new BatchTransformProgress(fileNodes.size()); + for (FileNode fileNode : fileNodes) { + transform(fileNode); + } + } + + private void transform(final FileNode fileNode) { + + threadPoolExecutor.execute(new Runnable() { + @Override + public void run() { + TransformResultInfo transformResult = CompileTransformUtil.compileFile(fileNode); + transformResults.put(fileNode, transformResult); + updateTransformProgress(); + } + }); + } + + private synchronized void updateTransformProgress() { + progress.updateProgress(); + updateCallBack.updateProgress(progress.getProgress()); + } + + public void shutDown() { + if (threadPoolExecutor != null) { + threadPoolExecutor.shutdownNow(); + } + threadPoolExecutor = PluginContexts.currentContext().newFixedThreadPool(MAXPOOLSIZE, + new ThreadFactoryBuilder().setNameFormat(THREAD_NAME_TEMPLATE).build()); + updateCallBack.shutDown(); + } + + public Map getResults() { + return transformResults; + } + +} diff --git a/designer-base/src/main/java/com/fr/nx/app/designer/transform/UpdateCallBack.java b/designer-base/src/main/java/com/fr/nx/app/designer/transform/UpdateCallBack.java new file mode 100644 index 000000000..85e1a1151 --- /dev/null +++ b/designer-base/src/main/java/com/fr/nx/app/designer/transform/UpdateCallBack.java @@ -0,0 +1,22 @@ +package com.fr.nx.app.designer.transform; + +/** + * Created by kerry on 2019-12-10 + */ +public interface UpdateCallBack { + /** + * 更新进度 + * @param progress 进度 + */ + void updateProgress(double progress); + + /** + * 进度中断 + */ + void shutDown(); + + /** + * 进度重置 + */ + void reset(); +} diff --git a/designer-base/src/main/java/com/fr/nx/app/designer/transform/ui/BatchTransformDialog.java b/designer-base/src/main/java/com/fr/nx/app/designer/transform/ui/BatchTransformDialog.java new file mode 100644 index 000000000..75b32cb27 --- /dev/null +++ b/designer-base/src/main/java/com/fr/nx/app/designer/transform/ui/BatchTransformDialog.java @@ -0,0 +1,61 @@ +package com.fr.nx.app.designer.transform.ui; + +import com.fr.design.gui.ibutton.UIButton; +import com.fr.design.layout.FRGUIPaneFactory; +import com.fr.design.utils.gui.GUICoreUtils; +import com.fr.locale.InterProviderFactory; + +import javax.swing.JDialog; +import javax.swing.JPanel; +import java.awt.BorderLayout; +import java.awt.Dimension; +import java.awt.Frame; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; + +/** + * Created by kerry on 2019-12-19 + */ +public class BatchTransformDialog extends JDialog implements ActionListener { + private UIButton closeBtn; + + public BatchTransformDialog(Frame parent, JPanel contentPane) { + super(parent, true); + + this.setTitle(InterProviderFactory.getProvider().getLocText("Fine-Plugin_Engine_Batch_Transform")); + this.setResizable(false); + JPanel defaultPane = FRGUIPaneFactory.createBorderLayout_L_Pane(); + this.setContentPane(defaultPane); + + closeBtn = new UIButton(com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Basic_Close")); + closeBtn.addActionListener(this); + JPanel buttonPanel = FRGUIPaneFactory.createRightFlowInnerContainer_S_Pane(); + buttonPanel.add(closeBtn); + + defaultPane.add(contentPane, BorderLayout.CENTER); + defaultPane.add(buttonPanel, BorderLayout.SOUTH); + + addWindowListener(new WindowAdapter() { + public void windowClosing(WindowEvent e) { + dialogExit(); + } + }); + + this.getRootPane().setDefaultButton(closeBtn); + + this.setSize(new Dimension(900, 600)); + GUICoreUtils.centerWindow(this); + } + + @Override + public void actionPerformed(ActionEvent e) { + dialogExit(); + } + + + private void dialogExit() { + this.dispose(); + } +} diff --git a/designer-base/src/main/java/com/fr/nx/app/designer/transform/ui/BatchTransformPane.java b/designer-base/src/main/java/com/fr/nx/app/designer/transform/ui/BatchTransformPane.java new file mode 100644 index 000000000..c11e4e4ad --- /dev/null +++ b/designer-base/src/main/java/com/fr/nx/app/designer/transform/ui/BatchTransformPane.java @@ -0,0 +1,223 @@ +package com.fr.nx.app.designer.transform.ui; + +import com.fr.base.extension.FileExtension; +import com.fr.design.dialog.BasicPane; +import com.fr.design.file.NodeAuthProcessor; +import com.fr.design.gui.ibutton.UIButton; +import com.fr.design.gui.icontainer.UIScrollPane; +import com.fr.design.gui.ilable.UILabel; +import com.fr.design.gui.itextfield.UITextField; +import com.fr.design.gui.itree.filetree.FileNodeComparator; +import com.fr.design.gui.itree.filetree.FileNodeConstants; +import com.fr.design.gui.itree.refreshabletree.ExpandMutableTreeNode; +import com.fr.design.layout.FRGUIPaneFactory; +import com.fr.design.mainframe.DesignerContext; +import com.fr.design.mainframe.DesignerFrame; +import com.fr.file.filetree.FileNode; +import com.fr.file.filetree.IOFileNodeFilter; +import com.fr.locale.InterProviderFactory; +import com.fr.log.FineLoggerFactory; +import com.fr.nx.app.designer.toolbar.TransformResultInfo; + +import javax.swing.BorderFactory; +import javax.swing.JPanel; +import javax.swing.tree.DefaultTreeModel; +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.Dialog; +import java.awt.Dimension; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.KeyAdapter; +import java.awt.event.KeyEvent; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +/** + * Created by kerry on 2019-12-10 + */ +public class BatchTransformPane extends BasicPane { + private UITextField searchField; + private TransformFileTree tree; + private Dialog showDialog; + private TransformPreparePane preparePane; + private TransformResultPane resultPane; + + + public BatchTransformPane() { + DesignerFrame designerFrame = DesignerContext.getDesignerFrame(); + this.showDialog = new BatchTransformDialog(designerFrame, this); + initPane(); + } + + private void initPane() { + this.setLayout(FRGUIPaneFactory.createBorderLayout()); + initNorthPane(); + initSouthPane(); + } + + private void initNorthPane() { + UILabel northTip = new UILabel(InterProviderFactory.getProvider().getLocText("Fine-Plugin_Engine_Transform_Tip")); + northTip.setForeground(Color.decode("#8F8F92")); + northTip.setBorder(BorderFactory.createEmptyBorder(0, 550, 10, 10)); + this.add(northTip, BorderLayout.NORTH); + } + + private void initSouthPane() { + preparePane = new TransformPreparePane(this.showDialog, this); + tree = new TransformFileTree(preparePane); + this.add(preparePane, BorderLayout.CENTER); + initLeftPane(); + initRightPane(); + } + + private void initLeftPane() { + IOFileNodeFilter filter = new IOFileNodeFilter(new String[]{FileExtension.CPT.getSuffix()}); + tree.setDigIn(true); + tree.setFileNodeFilter(filter); + UIScrollPane scrollPane = new UIScrollPane(tree); + scrollPane.setPreferredSize(new Dimension(320, 442)); + scrollPane.setBorder(BorderFactory.createEmptyBorder(12, 0, 0, 0)); + tree.refreshEnv(); + JPanel selectPane = FRGUIPaneFactory.createTitledBorderPaneCenter( + InterProviderFactory.getProvider().getLocText("Fine-Plugin_Engine_Transform_Select_Template")); + JPanel searchPane = initSearchPane(); + JPanel jPanel = FRGUIPaneFactory.createBorderLayout_S_Pane(); + jPanel.add(searchPane, BorderLayout.NORTH); + jPanel.add(scrollPane, BorderLayout.WEST); + jPanel.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0)); + selectPane.add(jPanel); + selectPane.setPreferredSize(new Dimension(330, 501)); + this.add(selectPane, BorderLayout.WEST); + } + + + private void initRightPane() { + resultPane = new TransformResultPane(); + this.add(resultPane, BorderLayout.EAST); + } + + private JPanel initSearchPane() { + JPanel jPanel = FRGUIPaneFactory.createNormalFlowInnerContainer_M_Pane(); + jPanel.setBorder(BorderFactory.createEmptyBorder()); + searchField = new UITextField(); + searchField.requestFocus(); + searchField.addKeyListener(keyFieldKeyListener); + searchField.setPreferredSize(new Dimension(261, 20)); + jPanel.add(searchField); + UIButton searchBtn = new UIButton(com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Basic_Search")); + searchBtn.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + search(); + } + }); + searchBtn.setPreferredSize(new Dimension(44, 20)); + jPanel.add(searchBtn); + return jPanel; + } + + + private KeyAdapter keyFieldKeyListener = new KeyAdapter() { + + @Override + public void keyPressed(KeyEvent e) { + if (e.getKeyCode() == KeyEvent.VK_ENTER) { + search(); + e.consume(); + } + } + }; + + + + private void search() { + //重新构建TreeModel + filter(searchField.getText()); + ExpandMutableTreeNode rootNode = (ExpandMutableTreeNode) tree.getModel().getRoot(); + ((DefaultTreeModel) tree.getModel()).reload(rootNode); + // 展开所有isExpanded为true的TreeNode + rootNode.expandCurrentTreeNode(tree); + } + + public void filter(String filterString) { + NodeAuthProcessor.getInstance().refresh(); + DefaultTreeModel defaultTreeModel = (DefaultTreeModel) tree.getModel(); + ExpandMutableTreeNode rootTreeNode = (ExpandMutableTreeNode) defaultTreeModel.getRoot(); + NodeAuthProcessor.getInstance().fixTreeNodeAuth(rootTreeNode); + filter(rootTreeNode, filterString); + defaultTreeModel.reload(rootTreeNode); + } + + private void filter(ExpandMutableTreeNode rootTreeNode, String filterString) { + rootTreeNode.removeAllChildren(); + + FileNode[] fns; + fns = listFileNodes(rootTreeNode); + + ExpandMutableTreeNode[] subTreeNodes = NodeAuthProcessor.getInstance().parser2TreeNodeArray(fns); + + for (ExpandMutableTreeNode node : subTreeNodes) { + filter(node, filterString); + if (node.getChildCount() > 0 || (node.getChildCount() == 0 && node.toString().contains(filterString))) { + node.setExpanded(true); + rootTreeNode.add(node); + } + } + + } + + private FileNode[] listFileNodes(ExpandMutableTreeNode rootTreeNode) { + if (rootTreeNode == null) { + return new FileNode[0]; + } + + Object object = rootTreeNode.getUserObject(); + if (!(object instanceof FileNode)) { + return new FileNode[0]; + } + + FileNode[] fileNodes = null; + try { + fileNodes = tree.listFile(((FileNode) object).getEnvPath()); + } catch (Exception e) { + FineLoggerFactory.getLogger().error(e.getMessage(), e); + } + if (fileNodes == null) { + fileNodes = new FileNode[0]; + } + Arrays.sort(fileNodes, new FileNodeComparator(FileNodeConstants.getSupportFileTypes())); + return fileNodes; + } + + public void resetFilePaths(Map resultMap) { + this.tree.resetSelectedPaths(); + Iterator iterator = resultMap.keySet().iterator(); + List list = new ArrayList(); + while (iterator.hasNext()){ + FileNode node = iterator.next(); + list.add(node); + } + this.tree.addTransformedList(list); + this.tree.refresh(); + resultPane.populate(resultMap); + } + + public void removeSelectedNode(FileNode fileNode) { + String path = fileNode.getEnvPath(); + this.tree.removeSelectedPath(path); + } + + + @Override + protected String title4PopupWindow() { + return InterProviderFactory.getProvider().getLocText("Fine-Plugin_Engine_Batch_Transform"); + } + + public Dialog showDialog() { + return this.showDialog; + } +} diff --git a/designer-base/src/main/java/com/fr/nx/app/designer/transform/ui/PrepareTransformFileList.java b/designer-base/src/main/java/com/fr/nx/app/designer/transform/ui/PrepareTransformFileList.java new file mode 100644 index 000000000..22d6afcfd --- /dev/null +++ b/designer-base/src/main/java/com/fr/nx/app/designer/transform/ui/PrepareTransformFileList.java @@ -0,0 +1,79 @@ +package com.fr.nx.app.designer.transform.ui; + +import com.fr.base.BaseUtils; +import com.fr.design.constants.UIConstants; +import com.fr.design.gui.ilist.UIList; +import com.fr.design.gui.itree.filetree.FileTreeIcon; +import com.fr.file.filetree.FileNode; + +import javax.swing.DefaultListModel; +import javax.swing.Icon; +import javax.swing.JList; +import javax.swing.ListSelectionModel; +import javax.swing.SwingUtilities; +import java.awt.Rectangle; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.awt.event.MouseListener; + +/** + * Created by kerry on 2020-01-13 + */ +public class PrepareTransformFileList extends UIList { + private TransformPreparePane transformingPane; + private static final int DELETE_RANGE = 20; + + + public PrepareTransformFileList(final TransformPreparePane transformingPane) { + super(); + this.transformingPane = transformingPane; + this.setBackground(UIConstants.TREE_BACKGROUND); + this.setSelectionMode(ListSelectionModel.SINGLE_INTERVAL_SELECTION); + DefaultListModel listModel = new DefaultListModel(); + this.setModel(listModel); + this.setCellRenderer(new UIListControlCellRenderer() { + @Override + protected Icon getLeftLabelIcon(Object value) { + if (value instanceof FileNode) { + return FileTreeIcon.getIcon((FileNode) value); + } + return null; + } + + @Override + protected Icon getRightLabelIcon(Object value) { + return BaseUtils.readIcon("/com/fr/nx/app/designer/transform/tab_close.png"); + } + }); + this.addMouseListener(getListMouseListener()); + } + + + private MouseListener getListMouseListener() { + return new MouseAdapter() { + @Override + public void mouseReleased(MouseEvent evt) { + JList list = (JList) evt.getSource(); + int selectedIndex = list.getSelectedIndex(); + Rectangle rectangle = list.getCellBounds(selectedIndex, selectedIndex); + if (SwingUtilities.isLeftMouseButton(evt) + && pointInSelected(rectangle, evt.getX(), evt.getY())) { + Object value = PrepareTransformFileList.this.getSelectedValue(); + if (value != null) { + transformingPane.removeSelectedNode((FileNode) value); + } + } + } + }; + } + + private boolean pointInSelected(Rectangle rectangle, int x, int y) { + if (rectangle == null) { + return false; + } + return x >= rectangle.x + rectangle.width - DELETE_RANGE && + x <= rectangle.x + rectangle.width && + y >= rectangle.y && y <= rectangle.y + rectangle.height; + } +} + diff --git a/designer-base/src/main/java/com/fr/nx/app/designer/transform/ui/TransformFileTree.java b/designer-base/src/main/java/com/fr/nx/app/designer/transform/ui/TransformFileTree.java new file mode 100644 index 000000000..cbfcd253f --- /dev/null +++ b/designer-base/src/main/java/com/fr/nx/app/designer/transform/ui/TransformFileTree.java @@ -0,0 +1,204 @@ +package com.fr.nx.app.designer.transform.ui; + +import com.fr.base.FRContext; +import com.fr.base.extension.FileExtension; +import com.fr.design.gui.itree.checkboxtree.CheckBoxTree; +import com.fr.design.gui.itree.checkboxtree.CheckBoxTreeCellRenderer; +import com.fr.design.gui.itree.checkboxtree.CheckBoxTreeSelectionModel; +import com.fr.design.gui.itree.refreshabletree.ExpandMutableTreeNode; +import com.fr.design.remote.ui.tree.FileAuthorityTree; +import com.fr.file.filetree.FileNode; +import com.fr.general.ComparatorUtils; +import com.fr.nx.app.designer.transform.BatchTransformUtil; + +import javax.swing.tree.DefaultTreeModel; +import javax.swing.tree.TreeCellRenderer; +import javax.swing.tree.TreePath; +import java.awt.AlphaComposite; +import java.awt.Graphics; +import java.awt.Graphics2D; +import java.awt.event.MouseEvent; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +/** + * Created by kerry on 2020-01-13 + */ +public class TransformFileTree extends FileAuthorityTree { + private TransformPreparePane preparePane; + private static final float CHECKBOX_ENABLE_OPACITY = 0.4f; + + private List transformedList = new ArrayList(); + + public TransformFileTree(TransformPreparePane preparePane) { + this.preparePane = preparePane; + } + + public void addTransformedList(List removeList) { + this.transformedList.addAll(removeList); + } + + @Override + protected CheckBoxTree.Handler createHandler() { + return new CheckBoxTree.Handler(this) { + @Override + public void mousePressed(MouseEvent e) { + super.mousePressed(e); + TreePath treePath = this.getTreePathForMouseEvent(e); + if (treePath == null) { + return; + } + CheckBoxTreeSelectionModel selectionModel = _tree.getCheckBoxTreeSelectionModel(); + boolean selected = selectionModel.isPathSelected(treePath, selectionModel.isDigIn()); + ExpandMutableTreeNode lastPathComponent = (ExpandMutableTreeNode) treePath.getLastPathComponent(); + List selectedFileNodes = filterBatchFileNode(lastPathComponent); + preparePane.removeTransformNode(selectedFileNodes); + if (selected) { + preparePane.addTransformNode(selectedFileNodes); + } + + } + }; + } + + + private List filterBatchFileNode(ExpandMutableTreeNode treeNode) { + List fileNodeList = new ArrayList<>(); + int childCount = treeNode.getChildCount(); + if (childCount > 0) { + loadPendingChildTreeNode(treeNode); + int expandChildCount = treeNode.getChildCount(); + for (int i = 0; i < expandChildCount; i++) { + fileNodeList.addAll(filterBatchFileNode((ExpandMutableTreeNode) treeNode.getChildAt(i))); + } + } else { + FileNode fileNode = (FileNode) treeNode.getUserObject(); + boolean locked = fileNodeLocked(treeNode); + if (!fileNode.isDirectory() && !locked) { + fileNodeList.add((FileNode) treeNode.getUserObject()); + } + } + return fileNodeList; + } + + + private boolean fileNodeLocked(ExpandMutableTreeNode treeNode) { + Object userObj = treeNode.getUserObject(); + if (userObj instanceof FileNode) { + FileNode node = (FileNode) userObj; + String lock = node.getLock(); + if (lock != null && !node.getUserID().equals(lock)) { + return true; + } + } + return false; + } + + public void resetSelectedPaths() { + CheckBoxTreeSelectionModel selectionModel = this.getCheckBoxTreeSelectionModel(); + selectionModel.clearSelection(); + } + + @Override + public void selectCheckBoxPaths(String[] paths) { + if (paths == null || paths.length == 0) { + return; + } + + DefaultTreeModel model = (DefaultTreeModel) this.getModel(); + ExpandMutableTreeNode treeNode = (ExpandMutableTreeNode) model.getRoot(); + List res = new ArrayList<>(); + for (String path : paths) { + TreePath treePath = getSelectedPath(treeNode, path); + if (treePath != null) { + res.add(treePath); + } + } + // 勾选中这些结点 + this.getCheckBoxTreeSelectionModel().setSelectionPaths(res.toArray(new TreePath[0])); + } + + private TreePath getSelectedPath(ExpandMutableTreeNode treeNode, String path) { + //可以只在选中的path中选择 + for (int i = 0, len = treeNode.getChildCount(); i < len; i++) { + ExpandMutableTreeNode childTreeNode = (ExpandMutableTreeNode) treeNode.getChildAt(i); + if (childTreeNode.getChildCount() > 0) { + loadPendingChildTreeNode(childTreeNode); + TreePath treePath = getSelectedPath(childTreeNode, path); + if (treePath != null) { + return treePath; + } + } + if (!(childTreeNode.getUserObject() instanceof FileNode)) { + continue; + } + FileNode fileNode = (FileNode) childTreeNode.getUserObject(); + if (ComparatorUtils.equals(path, fileNode.getEnvPath())) { + DefaultTreeModel model = (DefaultTreeModel) this.getModel(); + return new TreePath(model.getPathToRoot(childTreeNode)); + } + } + return null; + } + + public void removeSelectedPath(String path) { + if (path == null) { + return; + } + DefaultTreeModel model = (DefaultTreeModel) this.getModel(); + ExpandMutableTreeNode treeNode = (ExpandMutableTreeNode) model.getRoot(); + TreePath selectedPath = getSelectedPath(treeNode, path); + if (selectedPath != null) { + this.getCheckBoxTreeSelectionModel().removeSelectionPath(selectedPath); + + } + } + + + @Override + public FileNode[] listFile(String path) { + // 支持插件扩展, 先从env的filter拿, 再从插件拿 + Set supportTypes = new HashSet(); + for (String temp : this.filter.getSupportedTypes()) { + supportTypes.add(FileExtension.parse(temp)); + } + FileNode[] fileNodes = FRContext.getFileNodes().list( + path, + supportTypes.toArray(new FileExtension[supportTypes.size()]) + ); + return BatchTransformUtil.filterTransformedFile(fileNodes, transformedList); + } + + @Override + public boolean isCheckBoxEnabled(TreePath path) { + ExpandMutableTreeNode treeNode = (ExpandMutableTreeNode) path.getLastPathComponent(); + return !fileNodeLocked(treeNode); + } + + @Override + protected CheckBoxTreeCellRenderer createCellRenderer(TreeCellRenderer renderer) { + final CheckBoxTreeCellRenderer checkBoxTreeCellRenderer = new CheckBoxTreeCellRenderer(renderer) { + @Override + public void paint(Graphics g) { + if (!_checkBox.isEnabled()) { + AlphaComposite composite = AlphaComposite.getInstance(AlphaComposite.SRC_OVER, CHECKBOX_ENABLE_OPACITY); + ((Graphics2D) g).setComposite(composite); + } + super.paint(g); + } + }; + addPropertyChangeListener(CELL_RENDERER_PROPERTY, new PropertyChangeListener() { + @Override + public void propertyChange(PropertyChangeEvent evt) { + checkBoxTreeCellRenderer.setActualTreeRenderer((TreeCellRenderer) evt.getNewValue()); + } + }); + return checkBoxTreeCellRenderer; + } + + +} diff --git a/designer-base/src/main/java/com/fr/nx/app/designer/transform/ui/TransformPreparePane.java b/designer-base/src/main/java/com/fr/nx/app/designer/transform/ui/TransformPreparePane.java new file mode 100644 index 000000000..efbded1b0 --- /dev/null +++ b/designer-base/src/main/java/com/fr/nx/app/designer/transform/ui/TransformPreparePane.java @@ -0,0 +1,130 @@ +package com.fr.nx.app.designer.transform.ui; + +import com.fr.design.gui.ibutton.UIButton; +import com.fr.design.gui.icontainer.UIScrollPane; +import com.fr.design.gui.ilable.UILabel; +import com.fr.design.layout.FRGUIPaneFactory; +import com.fr.file.filetree.FileNode; +import com.fr.locale.InterProviderFactory; +import com.fr.nx.app.designer.toolbar.TransformResultInfo; +import com.fr.nx.app.designer.transform.BatchTransformer; +import com.fr.nx.app.designer.transform.UpdateCallBack; + +import javax.swing.BorderFactory; +import javax.swing.Box; +import javax.swing.DefaultListModel; +import javax.swing.JPanel; +import java.awt.BorderLayout; +import java.awt.Dialog; +import java.awt.Dimension; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +/** + * Created by kerry on 2020-01-13 + */ +public class TransformPreparePane extends JPanel { + private static final String INITIAL_DISPLAY_TEXT = "0"; + private PrepareTransformFileList fileList; + private List selectedFileNodes; + private UpdateProgressDialog dialog; + private BatchTransformer transformer; + private UpdateCallBack updateProgressPane; + private Dialog parentDialog; + private UILabel transCountLabel; + private BatchTransformPane batchTransformPane; + private UIButton startTransform; + + public TransformPreparePane(Dialog parentDialog, BatchTransformPane batchTransformPane) { + this.batchTransformPane = batchTransformPane; + this.parentDialog = parentDialog; + this.setLayout(FRGUIPaneFactory.createBorderLayout()); + updateProgressPane = new UpdateProgressPane(this); + transformer = new BatchTransformer(updateProgressPane); + fileList = new PrepareTransformFileList(this); + selectedFileNodes = new ArrayList<>(); + initPane(); + } + + private void initPane() { + JPanel transformPane = FRGUIPaneFactory.createTitledBorderPaneCenter( + InterProviderFactory.getProvider().getLocText("Fine-Plugin_Engine_Template_To_Transform")); + transformPane.setPreferredSize(new Dimension(270, 501)); + startTransform = new UIButton(InterProviderFactory.getProvider().getLocText("Fine-Plugin_Engine_Transform_Start")); + startTransform.setEnabled(false); + addStartTransformListener(startTransform); + transCountLabel = new UILabel(INITIAL_DISPLAY_TEXT); + JPanel northPane = FRGUIPaneFactory.createLeftFlowZeroGapBorderPane(); + northPane.add(startTransform); + northPane.add(Box.createHorizontalStrut(160)); + northPane.add(transCountLabel); + transformPane.add(northPane, BorderLayout.NORTH); + UIScrollPane scrollPane = new UIScrollPane(fileList); + scrollPane.setPreferredSize(new Dimension(260, 442)); + scrollPane.setBorder(BorderFactory.createEmptyBorder(0, 5, 0, 0)); + transformPane.add(scrollPane, BorderLayout.CENTER); + this.add(transformPane, BorderLayout.CENTER); + } + + private void addStartTransformListener(UIButton startTransform) { + startTransform.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + //开始转换 + transformer.batchTransform(selectedFileNodes); + displayProgressPane(); + } + + + private void displayProgressPane() { + dialog = new UpdateProgressDialog(transformer, parentDialog, (JPanel) updateProgressPane); + dialog.setVisible(true); + } + }); + } + + public void addTransformNode(List fileNodeList) { + selectedFileNodes.addAll(fileNodeList); + buildFileList(); + + } + + public void removeTransformNode(List fileNodeList) { + selectedFileNodes.removeAll(fileNodeList); + buildFileList(); + } + + public void removeSelectedNode(FileNode fileNode) { + selectedFileNodes.remove(fileNode); + buildFileList(); + this.batchTransformPane.removeSelectedNode(fileNode); + } + + private void buildFileList() { + DefaultListModel listModel = new DefaultListModel(); + int count = selectedFileNodes.size(); + for (int i = count - 1; i >= 0; i--) { + listModel.addElement(selectedFileNodes.get(i)); + } + this.fileList.setModel(listModel); + startTransform.setEnabled(selectedFileNodes.size() > 0); + transCountLabel.setText(String.valueOf(selectedFileNodes.size())); + } + + public void complete() { + dialog.dialogExit(); + updateProgressPane.reset(); + Map resultMap = transformer.getResults(); + resetFileNodeList(); + this.batchTransformPane.resetFilePaths(resultMap); + } + + private void resetFileNodeList(){ + this.selectedFileNodes.clear(); + buildFileList(); + } + +} diff --git a/designer-base/src/main/java/com/fr/nx/app/designer/transform/ui/TransformResultList.java b/designer-base/src/main/java/com/fr/nx/app/designer/transform/ui/TransformResultList.java new file mode 100644 index 000000000..83eb53400 --- /dev/null +++ b/designer-base/src/main/java/com/fr/nx/app/designer/transform/ui/TransformResultList.java @@ -0,0 +1,70 @@ +package com.fr.nx.app.designer.transform.ui; + +import com.fr.base.BaseUtils; +import com.fr.design.constants.UIConstants; +import com.fr.design.gui.ilist.UIList; +import com.fr.design.gui.itree.filetree.FileTreeIcon; +import com.fr.file.filetree.FileNode; +import com.fr.nx.app.designer.toolbar.TransformResultInfo; + +import javax.swing.DefaultListModel; +import javax.swing.Icon; +import javax.swing.ListSelectionModel; +import java.util.Iterator; +import java.util.Map; + +/** + * Created by kerry on 2020-01-13 + */ +public class TransformResultList extends UIList { + private Map resultMap; + private static final Icon FAILED_ICON = BaseUtils.readIcon("/com/fr/nx/app/designer/transform/transform_failed.png"); + private static final Icon SUCCESS_ICON = BaseUtils.readIcon("/com/fr/nx/app/designer/transform/transform_success.png"); + private static final Icon UNSUPPORT_ICON = BaseUtils.readIcon("/com/fr/nx/app/designer/transform/transform_unsupport.png"); + + public TransformResultList() { + super(); + this.setBackground(UIConstants.TREE_BACKGROUND); + this.setSelectionMode(ListSelectionModel.SINGLE_INTERVAL_SELECTION); + DefaultListModel listModel = new DefaultListModel(); + this.setModel(listModel); + this.setCellRenderer(new UIListControlCellRenderer() { + @Override + protected Icon getLeftLabelIcon(Object value) { + if (value instanceof FileNode) { + return FileTreeIcon.getIcon((FileNode) value); + } + return null; + } + + @Override + protected Icon getRightLabelIcon(Object value) { + if (value != null) { + TransformResultInfo resultInfo = resultMap.get(value); + switch (resultInfo.getResult()) { + case FAILED: + return FAILED_ICON; + case SUCCESS: + return SUCCESS_ICON; + case UNSUPPORT: + return UNSUPPORT_ICON; + } + } + return null; + } + }); + } + + public void populate(Map resultMap) { + this.resultMap = resultMap; + DefaultListModel listModel = new DefaultListModel(); + Iterator> iterator = resultMap.entrySet().iterator(); + while (iterator.hasNext()) { + Map.Entry entry = iterator.next(); + FileNode key = entry.getKey(); + listModel.addElement(key); + } + this.setModel(listModel); + } + +} diff --git a/designer-base/src/main/java/com/fr/nx/app/designer/transform/ui/TransformResultPane.java b/designer-base/src/main/java/com/fr/nx/app/designer/transform/ui/TransformResultPane.java new file mode 100644 index 000000000..6bed5aafc --- /dev/null +++ b/designer-base/src/main/java/com/fr/nx/app/designer/transform/ui/TransformResultPane.java @@ -0,0 +1,110 @@ +package com.fr.nx.app.designer.transform.ui; + +import com.fr.design.gui.icontainer.UIScrollPane; +import com.fr.design.gui.ilable.UILabel; +import com.fr.design.gui.itextarea.UITextArea; +import com.fr.design.layout.FRGUIPaneFactory; +import com.fr.file.filetree.FileNode; +import com.fr.general.ComparatorUtils; +import com.fr.locale.InterProviderFactory; +import com.fr.nx.app.designer.toolbar.TransformResult; +import com.fr.nx.app.designer.toolbar.TransformResultInfo; + +import javax.swing.BorderFactory; +import javax.swing.JPanel; +import javax.swing.event.ListSelectionEvent; +import javax.swing.event.ListSelectionListener; +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.Dimension; +import java.util.Iterator; +import java.util.Map; + +/** + * Created by kerry on 2020-01-10 + */ +public class TransformResultPane extends JPanel { + private TransformResultList resultList; + private UITextArea resultInfo; + private Map resultMap; + private UILabel successTip; + + public TransformResultPane() { + this.setLayout(FRGUIPaneFactory.createBorderLayout()); + initPane(); + } + + private void initPane() { + JPanel resultPane = FRGUIPaneFactory.createTitledBorderPaneCenter( + InterProviderFactory.getProvider().getLocText("Fine-Plugin_Engine_Transform_Result")); + JPanel northPane = FRGUIPaneFactory.createBorderLayout_S_Pane(); + resultList = new TransformResultList(); + addValueChangeListener(); + UIScrollPane scrollPane = new UIScrollPane(resultList); + scrollPane.setPreferredSize(new Dimension(260, 260)); + scrollPane.setBorder(BorderFactory.createEmptyBorder(0, 5, 0, 0)); + successTip = new UILabel(""); + successTip.setBorder(BorderFactory.createEmptyBorder(5, 150, 5, 10)); + northPane.add(successTip, BorderLayout.NORTH); + northPane.add(scrollPane, BorderLayout.CENTER); + resultPane.add(northPane); + + JPanel resultInfoPane = FRGUIPaneFactory.createBorderLayout_S_Pane(); + resultInfoPane.setPreferredSize(new Dimension(270, 165)); + resultInfoPane.setBorder(BorderFactory.createEmptyBorder(5, 5, 10, 5)); + UILabel transformLogLabel = new UILabel( + InterProviderFactory.getProvider().getLocText("Fine-Plugin_Engine_Transform_Logs")); + transformLogLabel.setBorder(BorderFactory.createEmptyBorder(0, 0, 5, 0)); + resultInfoPane.add(transformLogLabel, BorderLayout.NORTH); + resultInfo = new UITextArea(); + resultInfo.setBackground(Color.white); + resultInfo.setLineWrap(true); + resultInfo.setWrapStyleWord(true); + resultInfo.setEditable(false); + UIScrollPane resultScrollPane = new UIScrollPane(resultInfo); + resultInfoPane.add(resultScrollPane, BorderLayout.CENTER); + this.add(resultPane, BorderLayout.NORTH); + this.add(resultInfoPane, BorderLayout.CENTER); + } + + private void addValueChangeListener() { + resultList.addListSelectionListener(new ListSelectionListener() { + @Override + public void valueChanged(ListSelectionEvent e) { + FileNode selectedValue = (FileNode) resultList.getSelectedValue(); + if (resultInfo != null && selectedValue != null) { + TransformResultInfo result = resultMap.get(selectedValue); + resultInfo.setText(result.getTransformLog()); + } + } + }); + } + + public void populate(Map resultMap) { + int count = getSuccessCount(resultMap); + String tip = InterProviderFactory.getProvider().getLocText( + "Fine-Plugin_Engine_Transform_Result_Tip", String.valueOf(count)); + successTip.setText(tip); + this.resultMap = resultMap; + resultList.populate(resultMap); + if (resultList.getModel().getSize() > 0) { + resultList.setSelectedIndex(0); + FileNode firstResult = (FileNode) resultList.getModel().getElementAt(0); + resultInfo.setText(resultMap.get(firstResult).getTransformLog()); + } + } + + private int getSuccessCount(Map resultMap) { + int count = 0; + Iterator> iterator = resultMap.entrySet().iterator(); + while (iterator.hasNext()) { + Map.Entry entry = iterator.next(); + TransformResultInfo resultInfo = entry.getValue(); + if (ComparatorUtils.equals(TransformResult.SUCCESS, resultInfo.getResult())) { + count++; + } + } + return count; + } + +} diff --git a/designer-base/src/main/java/com/fr/nx/app/designer/transform/ui/UIListControlCellRenderer.java b/designer-base/src/main/java/com/fr/nx/app/designer/transform/ui/UIListControlCellRenderer.java new file mode 100644 index 000000000..47f677238 --- /dev/null +++ b/designer-base/src/main/java/com/fr/nx/app/designer/transform/ui/UIListControlCellRenderer.java @@ -0,0 +1,86 @@ +package com.fr.nx.app.designer.transform.ui; + +import com.fr.design.gui.ilable.UILabel; +import com.fr.design.layout.FRGUIPaneFactory; +import com.fr.stable.StringUtils; +import sun.swing.DefaultLookup; + +import javax.swing.Icon; +import javax.swing.JList; +import javax.swing.JPanel; +import javax.swing.ListCellRenderer; +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.Component; +import java.awt.Dimension; + +/** + * Created by kerry on 2020-01-14 + */ +public class UIListControlCellRenderer extends JPanel implements ListCellRenderer { + private UILabel content; + private UILabel controlLabel; + private Color initialLabelForeground; + + public UIListControlCellRenderer() { + initPane(); + } + + private void initPane() { + this.setLayout(FRGUIPaneFactory.createBorderLayout()); + content = new UILabel(); + initialLabelForeground = content.getForeground(); + controlLabel = new UILabel(); + controlLabel.setPreferredSize(new Dimension(16, 20)); + this.add(content, BorderLayout.CENTER); + this.add(controlLabel, BorderLayout.EAST); + } + + @Override + public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) { + setComponentOrientation(list.getComponentOrientation()); + Color bg = null; + Color fg = null; + + JList.DropLocation dropLocation = list.getDropLocation(); + if (dropLocation != null + && !dropLocation.isInsert() + && dropLocation.getIndex() == index) { + + bg = DefaultLookup.getColor(this, ui, "List.dropCellBackground"); + fg = DefaultLookup.getColor(this, ui, "List.dropCellForeground"); + + isSelected = true; + } + + if (isSelected) { + setBackground(bg == null ? list.getSelectionBackground() : bg); + setForeground(fg == null ? list.getSelectionForeground() : fg); + content.setForeground(Color.WHITE); + + } else { + setBackground(list.getBackground()); + setForeground(list.getForeground()); + content.setForeground(initialLabelForeground); + } + + content.setText((value == null) ? StringUtils.EMPTY : value.toString()); + Icon leftLabelIcon = getLeftLabelIcon(value); + if (leftLabelIcon != null) { + content.setIcon(leftLabelIcon); + } + Icon rightLabelIcon = getRightLabelIcon(value); + if (rightLabelIcon != null) { + controlLabel.setIcon(rightLabelIcon); + } + return this; + } + + protected Icon getLeftLabelIcon(Object value) { + return null; + } + + protected Icon getRightLabelIcon(Object value) { + return null; + } +} diff --git a/designer-base/src/main/java/com/fr/nx/app/designer/transform/ui/UpdateProgressDialog.java b/designer-base/src/main/java/com/fr/nx/app/designer/transform/ui/UpdateProgressDialog.java new file mode 100644 index 000000000..6d9bb009b --- /dev/null +++ b/designer-base/src/main/java/com/fr/nx/app/designer/transform/ui/UpdateProgressDialog.java @@ -0,0 +1,70 @@ +package com.fr.nx.app.designer.transform.ui; + +import com.fr.design.gui.ibutton.UIButton; +import com.fr.design.layout.FRGUIPaneFactory; +import com.fr.design.utils.gui.GUICoreUtils; +import com.fr.locale.InterProviderFactory; +import com.fr.nx.app.designer.transform.BatchTransformer; + +import javax.swing.JDialog; +import javax.swing.JPanel; +import java.awt.BorderLayout; +import java.awt.Dialog; +import java.awt.Dimension; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; + +/** + * Created by kerry on 2019-12-19 + */ +public class UpdateProgressDialog extends JDialog implements ActionListener { + + private UIButton pauseBtn; + private BatchTransformer transformer; + + public UpdateProgressDialog(BatchTransformer transformer, Dialog parent, JPanel contentPane) { + super(parent, true); + this.transformer = transformer; + initPane(contentPane); + } + + private void initPane(JPanel contentPane) { + this.setTitle(InterProviderFactory.getProvider().getLocText("Fine-Plugin_Engine_Format_Transform")); + this.setResizable(false); + JPanel defaultPane = FRGUIPaneFactory.createBorderLayout_L_Pane(); + this.setContentPane(defaultPane); + + pauseBtn = new UIButton(InterProviderFactory.getProvider().getLocText("Fine-Plugin_Engine_Transform_Suspend")); + pauseBtn.addActionListener(this); + JPanel buttonPanel = FRGUIPaneFactory.createRightFlowInnerContainer_S_Pane(); + buttonPanel.add(pauseBtn); + + defaultPane.add(contentPane, BorderLayout.CENTER); + defaultPane.add(buttonPanel, BorderLayout.SOUTH); + + addWindowListener(new WindowAdapter() { + @Override + public void windowClosing(WindowEvent e) { + transformer.shutDown(); + dialogExit(); + } + }); + + this.getRootPane().setDefaultButton(pauseBtn); + + this.setSize(new Dimension(262, 122)); + GUICoreUtils.centerWindow(this); + } + + @Override + public void actionPerformed(ActionEvent e) { + transformer.shutDown(); + dialogExit(); + } + + public void dialogExit() { + this.dispose(); + } +} diff --git a/designer-base/src/main/java/com/fr/nx/app/designer/transform/ui/UpdateProgressPane.java b/designer-base/src/main/java/com/fr/nx/app/designer/transform/ui/UpdateProgressPane.java new file mode 100644 index 000000000..5fb57c369 --- /dev/null +++ b/designer-base/src/main/java/com/fr/nx/app/designer/transform/ui/UpdateProgressPane.java @@ -0,0 +1,72 @@ +package com.fr.nx.app.designer.transform.ui; + + +import com.fr.base.BaseUtils; +import com.fr.decision.update.data.UpdateConstants; +import com.fr.design.dialog.BasicPane; +import com.fr.design.gui.ilable.UILabel; +import com.fr.design.layout.FRGUIPaneFactory; +import com.fr.general.ComparatorUtils; +import com.fr.locale.InterProviderFactory; +import com.fr.nx.app.designer.transform.UpdateCallBack; +import com.sun.java.swing.plaf.motif.MotifProgressBarUI; + +import javax.swing.BorderFactory; +import javax.swing.JPanel; +import javax.swing.JProgressBar; +import java.awt.BorderLayout; +import java.awt.Dimension; + + +/** + * Created by kerry on 2019-12-11 + */ +public class UpdateProgressPane extends BasicPane implements UpdateCallBack { + private TransformPreparePane contentPane; + private JProgressBar progressBar; + + public UpdateProgressPane(TransformPreparePane contentPane) { + this.contentPane = contentPane; + initPane(); + } + + private void initPane() { + this.setPreferredSize(new Dimension(262, 60)); + UILabel icon = new UILabel(BaseUtils.readIcon("/com/fr/nx/app/designer/transform/transforming.png")); + icon.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 10)); + this.add(icon, BorderLayout.WEST); + JPanel centerPane = FRGUIPaneFactory.createBorderLayout_S_Pane(); + UILabel title = new UILabel(InterProviderFactory.getProvider().getLocText("Fine-Plugin_Engine_Transforming")); + title.setBorder(BorderFactory.createEmptyBorder(0, 0, 8, 0)); + centerPane.add(title, BorderLayout.NORTH); + progressBar = new JProgressBar(); + progressBar.setUI(new MotifProgressBarUI()); + progressBar.setForeground(UpdateConstants.BAR_COLOR); + centerPane.add(progressBar, BorderLayout.CENTER); + this.add(centerPane, BorderLayout.CENTER); + } + + + @Override + public void updateProgress(double progress) { + progressBar.setValue((int) (progress * 100)); + if (ComparatorUtils.equals(progress, 1D)) { + contentPane.complete(); + } + } + + @Override + public void shutDown() { + contentPane.complete(); + } + + @Override + public void reset() { + progressBar.setValue(0); + } + + @Override + protected String title4PopupWindow() { + return InterProviderFactory.getProvider().getLocText("Fine-Plugin_Engine_Transform_Suspend"); + } +} diff --git a/designer-base/src/main/java/com/fr/nx/app/designer/utils/CompileTransformUtil.java b/designer-base/src/main/java/com/fr/nx/app/designer/utils/CompileTransformUtil.java new file mode 100644 index 000000000..3de788cd1 --- /dev/null +++ b/designer-base/src/main/java/com/fr/nx/app/designer/utils/CompileTransformUtil.java @@ -0,0 +1,55 @@ +package com.fr.nx.app.designer.utils; + +import com.fr.design.mainframe.JTemplate; +import com.fr.file.FILE; +import com.fr.file.filetree.FileNode; +import com.fr.general.ComparatorUtils; +import com.fr.log.FineLoggerFactory; +import com.fr.main.impl.WorkBook; +import com.fr.nx.app.designer.toolbar.TemplateTransformer; +import com.fr.nx.app.designer.toolbar.TransformResult; +import com.fr.nx.app.designer.toolbar.TransformResultInfo; +import com.fr.workspace.WorkContext; + +import java.io.InputStream; + +import static com.fr.base.extension.FileExtension.CPT; +import static com.fr.base.extension.FileExtension.CPTX; + +/** + * Created by kerry on 2019-12-04 + */ +public class CompileTransformUtil { + + + public static TransformResultInfo compileFile(FileNode fileNode) { + long start = System.currentTimeMillis(); + InputStream in = WorkContext.getWorkResource().openStream(fileNode.getEnvPath()); + WorkBook workBook = new WorkBook(); + TransformResultInfo result = null; + try { + workBook.readStream(in); + FILE file = TemplateTransformer.createOutputFile(fileNode.getEnvPath(), CPT.getSuffix(), CPTX.getSuffix()); + result = TemplateTransformer.compileCPTX(workBook, file); + } catch (Exception ignore) { + result = TransformResultInfo.generateResult(TransformResult.FAILED); + } + long end = System.currentTimeMillis(); + FineLoggerFactory.getLogger().debug(fileNode.getName() + " compile cost : " + (end - start) +" ms "); + return result; + } + + + public static FILE getTargetFile(JTemplate jTemplate, TemplateTransformer transformer) { + FILE file = null; + if (ComparatorUtils.equals(TemplateTransformer.TO_CPTX, transformer)) { + file = TemplateTransformer.createOutputFile(jTemplate.getEditingFILE().getPath(), + CPT.getSuffix(), CPTX.getSuffix()); + + } else if (ComparatorUtils.equals(TemplateTransformer.TO_CPT, transformer)) { + file = TemplateTransformer.createOutputFile(jTemplate.getEditingFILE().getPath(), + CPTX.getSuffix(), CPT.getSuffix()); + } + return file; + } +} diff --git a/designer-base/src/main/java/com/fr/nx/app/designer/utils/DesignerCptxFileUtils.java b/designer-base/src/main/java/com/fr/nx/app/designer/utils/DesignerCptxFileUtils.java new file mode 100644 index 000000000..a51e04768 --- /dev/null +++ b/designer-base/src/main/java/com/fr/nx/app/designer/utils/DesignerCptxFileUtils.java @@ -0,0 +1,79 @@ +package com.fr.nx.app.designer.utils; + +import com.fr.file.FILE; +import com.fr.general.CommonIOUtils; +import com.fr.log.FineLoggerFactory; +import com.fr.main.impl.WorkBook; +import com.fr.nx.marshal.impl.xml.DefaultXMLObjectUnmarshaler; +import com.fr.nx.marshal.util.MarshalUtil; +import com.fr.nx.cptx.entry.metadata.CptxMetadata; +import com.fr.nx.cptx.io.handle.impl.OriginCptProvider; +import com.fr.nx.cptx.marshal.util.CptxMarshalUtil; +import com.fr.nx.cptx.utils.CptxFileUtils; +import com.fr.stable.EncodeConstants; +import com.fr.stable.StableUtils; +import com.fr.stable.xml.XMLableReader; +import com.fr.workspace.WorkContext; + +import java.io.ByteArrayOutputStream; +import java.io.InputStream; +import java.io.InputStreamReader; + +/** + * Created by loy on 2021/1/18. + * + *

用于设计器的cptx的一些工具类 + *

+ */ +public class DesignerCptxFileUtils { + + /** + * 根据file来获取cptx中的cpt + * @param file + * @return + */ + public static WorkBook getWorkBook(final FILE file){ + try (InputStream input = file.asInputStream(); + ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) { + CommonIOUtils.copyBinaryTo(input, outputStream); + OriginCptProvider provider = new OriginCptProvider(outputStream.toByteArray()); + return CptxFileUtils.getWorkBook(provider); + } catch (Exception e) { + FineLoggerFactory.getLogger().error(e.getMessage(), e); + } + return null; + } + + /** + * 获取模板的元数据 + * @param file + * @return + */ + public static CptxMetadata getMetadata(FILE file){ + try { + String metadataPath = StableUtils.pathJoin(generateCompileDir(file), CptxMarshalUtil.NAME_METADATA); + if(!WorkContext.getWorkResource().exist(metadataPath)){ + return null; + } + InputStream metadataInput = WorkContext.getWorkResource().openStream(metadataPath); + XMLableReader reader = XMLableReader.createXMLableReader( + new InputStreamReader(metadataInput, EncodeConstants.ENCODING_UTF_8)); + CptxMetadata metadata = (CptxMetadata) new DefaultXMLObjectUnmarshaler().unmarshal( + MarshalUtil.createMarshalableExtra(CptxMetadata.class), reader); + return metadata; + } catch (Exception e) { + FineLoggerFactory.getLogger().error(e.getMessage(), e); + } + return null; + } + + /** + * 根据文件获取编译后的文件夹绝对路径,比如原文件位于reportlets/test/merge.cptx,转化后为evn://assets/engine/test/merge文件夹 + * @param file + * @return + */ + public static String generateCompileDir(FILE file){ + String filePath = file.getPath(); + return CptxFileUtils.generateCompileDir(filePath); + } +} diff --git a/designer-base/src/main/resources/com/fr/design/images/bbs/bbs_normal.svg b/designer-base/src/main/resources/com/fr/design/images/bbs/bbs_normal.svg index ae72bfa4d..4a9711e35 100644 --- a/designer-base/src/main/resources/com/fr/design/images/bbs/bbs_normal.svg +++ b/designer-base/src/main/resources/com/fr/design/images/bbs/bbs_normal.svg @@ -4,4 +4,4 @@ - + \ No newline at end of file diff --git a/designer-base/src/main/resources/com/fr/design/images/buttonicon/prewidget_normal.svg b/designer-base/src/main/resources/com/fr/design/images/buttonicon/prewidget_normal.svg new file mode 100644 index 000000000..542c02c26 --- /dev/null +++ b/designer-base/src/main/resources/com/fr/design/images/buttonicon/prewidget_normal.svg @@ -0,0 +1,8 @@ + + + icon_控件管理 有小箭头_normal + + + + + \ No newline at end of file diff --git a/designer-base/src/main/resources/com/fr/design/images/buttonicon/widget/files_up_normal.svg b/designer-base/src/main/resources/com/fr/design/images/buttonicon/widget/files_up_normal.svg new file mode 100644 index 000000000..34cd6083a --- /dev/null +++ b/designer-base/src/main/resources/com/fr/design/images/buttonicon/widget/files_up_normal.svg @@ -0,0 +1,7 @@ + + + icon_控件_文件控件_normal + + + + \ No newline at end of file diff --git a/designer-base/src/main/resources/com/fr/design/images/control/addPopup_disabled.svg b/designer-base/src/main/resources/com/fr/design/images/control/addPopup_disabled.svg new file mode 100644 index 000000000..d48e1a27b --- /dev/null +++ b/designer-base/src/main/resources/com/fr/design/images/control/addPopup_disabled.svg @@ -0,0 +1,16 @@ + + + 编组 + + + + + + + + + + + + + \ No newline at end of file diff --git a/designer-base/src/main/resources/com/fr/design/images/control/addPopup_normal.svg b/designer-base/src/main/resources/com/fr/design/images/control/addPopup_normal.svg new file mode 100644 index 000000000..8cec802fe --- /dev/null +++ b/designer-base/src/main/resources/com/fr/design/images/control/addPopup_normal.svg @@ -0,0 +1,14 @@ + + + 编组备份 + + + + + + + + + + + \ No newline at end of file diff --git a/designer-base/src/main/resources/com/fr/design/images/control/batch_esd_off.png b/designer-base/src/main/resources/com/fr/design/images/control/batch_esd_off.png new file mode 100644 index 000000000..0e8dce8ef Binary files /dev/null and b/designer-base/src/main/resources/com/fr/design/images/control/batch_esd_off.png differ diff --git a/designer-base/src/main/resources/com/fr/design/images/control/batch_esd_on.png b/designer-base/src/main/resources/com/fr/design/images/control/batch_esd_on.png new file mode 100644 index 000000000..2f519cf39 Binary files /dev/null and b/designer-base/src/main/resources/com/fr/design/images/control/batch_esd_on.png differ diff --git a/designer-base/src/main/resources/com/fr/design/images/control/edit_disabled.svg b/designer-base/src/main/resources/com/fr/design/images/control/edit_disabled.svg new file mode 100644 index 000000000..9b001cfc5 --- /dev/null +++ b/designer-base/src/main/resources/com/fr/design/images/control/edit_disabled.svg @@ -0,0 +1,8 @@ + + + icon_编辑_disable + + + + + \ No newline at end of file diff --git a/designer-base/src/main/resources/com/fr/design/images/control/edit_normal.svg b/designer-base/src/main/resources/com/fr/design/images/control/edit_normal.svg new file mode 100644 index 000000000..291719452 --- /dev/null +++ b/designer-base/src/main/resources/com/fr/design/images/control/edit_normal.svg @@ -0,0 +1,8 @@ + + + icon_编辑_normal + + + + + \ No newline at end of file diff --git a/designer-base/src/main/resources/com/fr/design/images/control/remove_disabled.svg b/designer-base/src/main/resources/com/fr/design/images/control/remove_disabled.svg new file mode 100644 index 000000000..b57f9a853 --- /dev/null +++ b/designer-base/src/main/resources/com/fr/design/images/control/remove_disabled.svg @@ -0,0 +1,11 @@ + + + icon_删除_normal + + + + + + + + \ No newline at end of file diff --git a/designer-base/src/main/resources/com/fr/design/images/control/remove_normal.svg b/designer-base/src/main/resources/com/fr/design/images/control/remove_normal.svg new file mode 100644 index 000000000..b09268f45 --- /dev/null +++ b/designer-base/src/main/resources/com/fr/design/images/control/remove_normal.svg @@ -0,0 +1,9 @@ + + + icon_删除_normal备份 + + + + + + \ No newline at end of file diff --git a/designer-base/src/main/resources/com/fr/design/images/m_file/preview_disabled.svg b/designer-base/src/main/resources/com/fr/design/images/m_file/preview_disabled.svg new file mode 100644 index 000000000..3edcffdc6 --- /dev/null +++ b/designer-base/src/main/resources/com/fr/design/images/m_file/preview_disabled.svg @@ -0,0 +1,11 @@ + + + icon_报表web属性_打印预览_normal + + + + + + + + \ No newline at end of file diff --git a/designer-base/src/main/resources/com/fr/design/images/m_file/preview_normal.svg b/designer-base/src/main/resources/com/fr/design/images/m_file/preview_normal.svg new file mode 100644 index 000000000..a29073712 --- /dev/null +++ b/designer-base/src/main/resources/com/fr/design/images/m_file/preview_normal.svg @@ -0,0 +1,9 @@ + + + icon_报表web属性_打印预览_normal备份 + + + + + + \ No newline at end of file diff --git a/designer-base/src/test/java/com/fr/nx/app/designer/toolbar/TemplateTransformerDebugTest.java b/designer-base/src/test/java/com/fr/nx/app/designer/toolbar/TemplateTransformerDebugTest.java new file mode 100644 index 000000000..6a8f8247b --- /dev/null +++ b/designer-base/src/test/java/com/fr/nx/app/designer/toolbar/TemplateTransformerDebugTest.java @@ -0,0 +1,239 @@ +package com.fr.nx.app.designer.toolbar; + +import com.fr.base.Parameter; +import com.fr.base.chart.BaseChartCollection; +import com.fr.base.io.XMLEncryptKey; +import com.fr.base.io.XMLEncryptUtils; +import com.fr.base.iofile.IOFileAttrMarkManager; +import com.fr.base.parameter.ParameterUI; +import com.fr.chart.chartattr.ChartCollection; +import com.fr.config.dao.DaoContext; +import com.fr.config.dao.impl.LocalClassHelperDao; +import com.fr.config.dao.impl.LocalEntityDao; +import com.fr.config.dao.impl.LocalXmlEntityDao; +import com.fr.file.MemFILE; +import com.fr.form.DefaultFormOperator; +import com.fr.form.FormOperator; +import com.fr.form.main.ExtraFormClassManager; +import com.fr.form.main.Form; +import com.fr.form.main.FormHyperlink; +import com.fr.form.main.parameter.FormParameterUI; +import com.fr.form.parameter.FormSubmitButton; +import com.fr.form.plugin.DefaultSwitcherImpl; +import com.fr.general.xml.GeneralXMLTools; +import com.fr.io.EncryptUtils; +import com.fr.js.FormHyperlinkProvider; +import com.fr.main.impl.WorkBook; +import com.fr.nx.calculable.Calculable; +import com.fr.nx.calculable.type.ConstantCalculable; +import com.fr.nx.cell.CellKey; +import com.fr.nx.cell.CellTemplate; +import com.fr.nx.cptx.cache.CptxTemplatePool; +import com.fr.nx.cptx.entry.CptxTemplate; +import com.fr.nx.cptx.resource.ImageResourceRef; +import com.fr.page.BaseSinglePagePrintable; +import com.fr.page.BaseSingleReportCache; +import com.fr.page.ClippedChartPage; +import com.fr.page.ClippedECPage; +import com.fr.page.ClippedPageProvider; +import com.fr.page.PDF2Painter; +import com.fr.page.PageGeneratorProvider; +import com.fr.page.PagePainter; +import com.fr.page.PagePainterProvider; +import com.fr.page.PageSetChainProvider; +import com.fr.page.PageSetProvider; +import com.fr.page.PageXmlOperator; +import com.fr.page.PageXmlProvider; +import com.fr.page.PaperSettingProvider; +import com.fr.page.ReportPage; +import com.fr.page.ReportPageAttrProvider; +import com.fr.page.ReportPageProvider; +import com.fr.page.SheetPage; +import com.fr.page.SinglePagePrintable; +import com.fr.page.SingleReportCache; +import com.fr.page.generator.PaginateReportPageGenerator; +import com.fr.page.generator.PolyReportPageGenerator; +import com.fr.page.generator.SheetPageGenerator; +import com.fr.page.pageset.ArrayPageSet; +import com.fr.page.pageset.PageSetChain; +import com.fr.page.stable.PaperSetting; +import com.fr.page.stable.ReportPageAttr; +import com.fr.plugin.attr.CalculatorAttrMark; +import com.fr.runtime.FineRuntime; +import com.fr.stable.EssentialUtils; +import com.fr.stable.bridge.BridgeMark; +import com.fr.stable.bridge.StableFactory; +import com.fr.stable.fun.WidgetSwitcher; +import com.fr.stable.module.Module; +import com.fr.stable.plugin.ExtraFormClassManagerProvider; +import com.fr.transaction.Configurations; +import com.fr.transaction.LocalConfigurationHelper; +import com.fr.xml.ReportXMLUtils; +import org.easymock.EasyMock; +import org.easymock.IArgumentMatcher; +import org.junit.Assert; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.powermock.api.easymock.PowerMock; +import org.powermock.core.classloader.annotations.PowerMockIgnore; +import org.powermock.core.classloader.annotations.PrepareForTest; +import org.powermock.core.classloader.annotations.SuppressStaticInitializationFor; +import org.powermock.modules.junit4.PowerMockRunner; +import org.powermock.reflect.Whitebox; + +import java.io.InputStream; + +@RunWith(PowerMockRunner.class) +@PrepareForTest({CptxTemplatePool.class, XMLEncryptUtils.class}) +@SuppressStaticInitializationFor({"com.fr.nx.cptx.cache.CptxTemplatePool", "com.fr.base.io.XMLEncryptUtils"}) +@PowerMockIgnore({"com.sun.tools.attach.*", "sun.tools.attach.*"}) +public class TemplateTransformerDebugTest { + + @BeforeClass + public static void beforeClass() { + System.setProperty("apple.awt.UIElement", "true"); + StableFactory.registerXMLDescription(ReportPageAttrProvider.XML_TAG, new ReportPageAttr()); + StableFactory.registerMarkedClass(ReportPageProvider.XML_TAG, ReportPage.class); + StableFactory.registerMarkedClass(PaperSettingProvider.XML_TAG, PaperSetting.class); + StableFactory.registerMarkedClass(ReportPageProvider.XML_TAG_4_SHEET, SheetPage.class); + StableFactory.registerMarkedClass(PagePainterProvider.XML_TAG, PagePainter.class); + StableFactory.registerMarkedClass(PageSetChainProvider.XML_TAG, PageSetChain.class); + StableFactory.registerMarkedClass(PageXmlProvider.XML_TAG, PageXmlOperator.class); + StableFactory.registerMarkedClass(BaseSinglePagePrintable.XML_TAG, SinglePagePrintable.class); + StableFactory.registerMarkedClass(BaseSingleReportCache.XML_TAG, SingleReportCache.class); + StableFactory.registerMarkedClass(PageSetProvider.XML_TAG_4_ARRAY, ArrayPageSet.class); + StableFactory.registerMarkedClass(ClippedPageProvider.XML_TAG_EC, ClippedECPage.class); + StableFactory.registerMarkedClass(ClippedPageProvider.XML_TAG_CHART, ClippedChartPage.class); + StableFactory.registerMarkedClass(PageGeneratorProvider.XML_TAG_PAGE, PaginateReportPageGenerator.class); + StableFactory.registerMarkedClass(PageGeneratorProvider.XML_TAG_POLY, PolyReportPageGenerator.class); + StableFactory.registerMarkedClass(PageGeneratorProvider.XML_TAG_SHEET, SheetPageGenerator.class); + StableFactory.registerMarkedClass(PagePainterProvider.XML_TAG_4_PDF, PDF2Painter.class); + StableFactory.registerMarkedClass(BridgeMark.SUBMIT_BUTTON, FormSubmitButton.class); + StableFactory.registerMarkedClass(FormOperator.MARK_STRING, DefaultFormOperator.class); + StableFactory.registerMarkedClass(Module.FORM_MODULE, Form.class); + StableFactory.registerMarkedClass(ParameterUI.FORM_XML_TAG, FormParameterUI.class); + StableFactory.registerMarkedClass(FormHyperlinkProvider.XML_TAG, FormHyperlink.class); + StableFactory.registerMarkedObject(WidgetSwitcher.XML_TAG, new DefaultSwitcherImpl()); + StableFactory.registerMarkedClass(ExtraFormClassManagerProvider.XML_TAG, ExtraFormClassManager.class); + StableFactory.registerXMLDescription(BaseChartCollection.XML_TAG, new ChartCollection()); + StableFactory.registerXMLDescription(Parameter.XML_TAG, new Parameter()); + FineRuntime.start(); + GeneralXMLTools.Object_Tokenizer = new ReportXMLUtils.ReportObjectTokenizer(); + GeneralXMLTools.Object_XML_Writer_Finder = new ReportXMLUtils.ReportObjectXMLWriterFinder(); + DaoContext.setEntityDao(new LocalEntityDao()); + DaoContext.setClassHelperDao(new LocalClassHelperDao()); + DaoContext.setXmlEntityDao(new LocalXmlEntityDao()); + Configurations.setHelper(new LocalConfigurationHelper()); + + IOFileAttrMarkManager.register(new CalculatorAttrMark()); + } + + @Before + public void before() { + Whitebox.setInternalState(XMLEncryptUtils.class, "KEY", new XMLEncryptKey()); + } + + @Test + public void testUnsupportedCompile() { + WorkBook workbook = readCpt("read-write-expand-order.cpt"); + + CptxTemplatePool pool = EasyMock.mock(CptxTemplatePool.class); + PowerMock.mockStatic(CptxTemplatePool.class); + EasyMock.expect(CptxTemplatePool.getInstance()).andReturn(pool).anyTimes(); + + PowerMock.replay(CptxTemplatePool.class); + pool.addCptxTemplate(EasyMock.anyString(), matchUnsupportedWebPreviewCptxTemplate()); + EasyMock.expectLastCall().once(); + + EasyMock.replay(pool); + TemplateTransformer.compileCPTX(workbook, new MemFILE("read-write-expand-order.cpt")); + + EasyMock.verify(pool); + PowerMock.verify(CptxTemplatePool.class); + + } + + @Test + public void testImageRefCompile() { + WorkBook workbook = readCpt("read-write-image-ref.cpt"); + + CptxTemplatePool pool = EasyMock.mock(CptxTemplatePool.class); + PowerMock.mockStatic(CptxTemplatePool.class); + EasyMock.expect(CptxTemplatePool.getInstance()).andReturn(pool).anyTimes(); + + PowerMock.replay(CptxTemplatePool.class); + pool.addCptxTemplate(EasyMock.anyString(), matchImageRefWebPreviewCptxTemplate()); + EasyMock.expectLastCall().once(); + + EasyMock.replay(pool); + TemplateTransformer.compileCPTX(workbook, new MemFILE("read-write-image-ref.cpt")); + + EasyMock.verify(pool); + PowerMock.verify(CptxTemplatePool.class); + + } + + private CptxTemplate matchImageRefWebPreviewCptxTemplate() { + EasyMock.reportMatcher(new IArgumentMatcher() { + @Override + public boolean matches(Object argument) { + if (argument instanceof CptxTemplate) { + CptxTemplate cptxTemplate = (CptxTemplate) argument; + Assert.assertNotNull(cptxTemplate.getTemplate()); + Assert.assertNotNull(cptxTemplate.getTemplate().getCompileResult()); + Assert.assertEquals(1, cptxTemplate.getTemplate().getCompileResult().getCellBlocks().length); + Assert.assertNotNull(cptxTemplate.getTemplate().getCompileResult().getCellBlocks()[0].getDataStructure()); + Assert.assertNotNull(cptxTemplate.getTemplate().getCompileResult().getCellBlocks()[0].getDataStructure().getCells()); + CellTemplate cellTemplate = cptxTemplate.getTemplate().getCompileResult().getCellBlocks()[0].getDataStructure().getCells().get(CellKey.fromString("A2")); + Assert.assertNotNull(cellTemplate); + Calculable calculable = cellTemplate.getCalculable(); + ConstantCalculable constantCalculable = calculable.unWrap(ConstantCalculable.KEY); + Assert.assertNotNull(constantCalculable); + Assert.assertTrue(constantCalculable.get() instanceof ImageResourceRef); + return true; + } + return false; + } + + @Override + public void appendTo(StringBuffer buffer) { + buffer.append("cptx template should be previewed on the web side but not."); + } + }); + return null; + } + + private CptxTemplate matchUnsupportedWebPreviewCptxTemplate() { + EasyMock.reportMatcher(new IArgumentMatcher() { + @Override + public boolean matches(Object argument) { + if (argument instanceof CptxTemplate) { + CptxTemplate cptxTemplate = (CptxTemplate) argument; + Assert.assertEquals("unsupported feature: sort after expand", + cptxTemplate.getMetadata().getFailMessage()); + return true; + } + return false; + } + + @Override + public void appendTo(StringBuffer buffer) { + buffer.append("should find unsupported error message but not"); + } + }); + return null; + } + + private static WorkBook readCpt(String subPath) { + InputStream is = TemplateTransformerDebugTest.class.getClassLoader().getResourceAsStream("cpt/" + subPath); + WorkBook wb = new WorkBook(); + try { + wb.readStream(EncryptUtils.decodeInputStream(is)); + } catch (Exception e) { + throw new RuntimeException(e); + } + return wb; + } +} diff --git a/designer-base/src/test/java/com/fr/nx/app/designer/toolbar/TemplateTransformerTest.java b/designer-base/src/test/java/com/fr/nx/app/designer/toolbar/TemplateTransformerTest.java new file mode 100644 index 000000000..e0afe9313 --- /dev/null +++ b/designer-base/src/test/java/com/fr/nx/app/designer/toolbar/TemplateTransformerTest.java @@ -0,0 +1,85 @@ +package com.fr.nx.app.designer.toolbar; + +import com.fr.base.FRContext; +import com.fr.base.operator.common.CommonOperator; +import com.fr.file.FILE; +import com.fr.file.filetree.FileNodes; +import com.fr.invoke.Reflect; +import org.easymock.EasyMock; +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.powermock.api.easymock.PowerMock; +import org.powermock.core.classloader.annotations.PowerMockIgnore; +import org.powermock.core.classloader.annotations.PrepareForTest; +import org.powermock.modules.junit4.PowerMockRunner; + +import static com.fr.nx.app.designer.toolbar.TemplateTransformer.OTHER; +import static com.fr.nx.app.designer.toolbar.TemplateTransformer.TO_CPT; +import static com.fr.nx.app.designer.toolbar.TemplateTransformer.TO_CPTX; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; + +@RunWith(PowerMockRunner.class) +@PrepareForTest({FRContext.class}) +@PowerMockIgnore({"sun.tools.attach.*", "com.sun.tools.*", "com.fr.license.*"}) +public class TemplateTransformerTest { + + @Test + public void testParse() { + assertEquals(TO_CPTX, TemplateTransformer.parse("/sdf/abc.cpt")); + assertEquals(TO_CPT, TemplateTransformer.parse("/sdf/abc.cptx")); + assertEquals(OTHER, TemplateTransformer.parse("/sdf/abc.frm")); + assertEquals(OTHER, TemplateTransformer.parse(null)); + assertEquals(OTHER, TemplateTransformer.parse("abc")); + } + + @Test + public void testCompileFailed() { + TransformResultInfo result = TemplateTransformer.compileCPTX(null, null); + assertEquals(result.getResult(), TransformResult.FAILED); + } + + @Test + public void testGenerateNewPath() { + assertEquals(generateNewPath("/abc/dd.cpt", ".cpt", ".cptx"), "/abc/dd.cptx"); + assertEquals(generateNewPath("/abc/dd.cptx", ".cpt", ".cptx"), "/abc/dd.cptx"); + assertEquals(generateNewPath("/abc/dd.cptx", ".cptx", ".cpt"), "/abc/dd.cpt"); + assertEquals(generateNewPath("/abc/dd.frm", ".cpt", ".cptx"), "/abc/dd.frm"); + assertNull(generateNewPath(null, null, null)); + assertEquals(generateNewPath("abc", null, null), "abc"); + } + + private String generateNewPath(String oldPath, String oldSuffix, String newSuffix) { + return Reflect.on(TemplateTransformer.class).call("generateNewPath", oldPath, oldSuffix, newSuffix).get(); + } + + @Test + public void testCreateOutputFile() { + CommonOperator commonOperator = EasyMock.mock(CommonOperator.class); + FileNodes fileNodes = EasyMock.mock(FileNodes.class); + EasyMock.expect(fileNodes.getSupportedTypes()).andReturn(new String[]{"cptx"}).anyTimes(); + PowerMock.mockStatic(FRContext.class); + EasyMock.expect(FRContext.getCommonOperator()).andReturn(commonOperator).anyTimes(); + EasyMock.expect(FRContext.getFileNodes()).andReturn(fileNodes).anyTimes(); + EasyMock.expect(commonOperator.getWebRootPath()).andReturn("/WebInf/").anyTimes(); + EasyMock.replay(commonOperator, fileNodes); + PowerMock.replayAll(); + FILE file1 = TemplateTransformer.createOutputFile("WorkBook1.cpt", ".cpt", ".cptx"); + FILE file2 = TemplateTransformer.createOutputFile("WorkBook1.cpt", ".cptx", ".cpt"); + assertEquals("WorkBook1.cptx", file1.getPath()); + assertEquals("WorkBook1.cpt", file2.getPath()); + } + + + @Test + public void testNeedDoAfterTransformed() { + Assert.assertTrue(needDoAfterTransformed(TransformResult.SUCCESS)); + Assert.assertTrue(needDoAfterTransformed(TransformResult.UNSUPPORT)); + Assert.assertFalse(needDoAfterTransformed(TransformResult.FAILED)); + } + + private boolean needDoAfterTransformed(TransformResult result) { + return Reflect.on(TemplateTransformer.class).call("needDoAfterTransformed", result).get(); + } +} diff --git a/designer-base/src/test/java/com/fr/nx/app/designer/toolbar/TransformResultInfoTest.java b/designer-base/src/test/java/com/fr/nx/app/designer/toolbar/TransformResultInfoTest.java new file mode 100644 index 000000000..66a32b95f --- /dev/null +++ b/designer-base/src/test/java/com/fr/nx/app/designer/toolbar/TransformResultInfoTest.java @@ -0,0 +1,32 @@ +package com.fr.nx.app.designer.toolbar; + +import com.fr.locale.InterProviderFactory; +import org.junit.Assert; +import org.junit.Test; + +/** + * Created by kerry on 2020-01-15 + */ +public class TransformResultInfoTest { + @Test + public void testGetTransformLog() { + TransformResultInfo resultInfo1 = TransformResultInfo.generateResult(TransformResult.FAILED, "failed"); + Assert.assertEquals("failed\n" + + InterProviderFactory.getProvider().getLocText("Fine-Plugin_Engine_Transform_Failed_Tip"), + resultInfo1.getTransformLog()); + TransformResultInfo resultInfo2 = TransformResultInfo.generateResult(TransformResult.SUCCESS); + Assert.assertEquals(InterProviderFactory.getProvider().getLocText("Fine-Plugin_Engine_Transform_Success_Tip"), + resultInfo2.getTransformLog()); + TransformResultInfo resultInfo3 = TransformResultInfo.generateResult(TransformResult.UNSUPPORT, "unsupport"); + Assert.assertEquals("unsupport\n" + + InterProviderFactory.getProvider().getLocText("Fine-Plugin_Engine_Transform_Unsupport_Tip"), + resultInfo3.getTransformLog()); + + } + + @Test + public void testSaved() { + TransformResultInfo resultInfo1 = TransformResultInfo.generateResult(TransformResult.FAILED, "failed").saved(false); + Assert.assertFalse(resultInfo1.isSaved()); + } +} diff --git a/designer-base/src/test/java/com/fr/nx/app/designer/transform/BatchTransformProgressTest.java b/designer-base/src/test/java/com/fr/nx/app/designer/transform/BatchTransformProgressTest.java new file mode 100644 index 000000000..c0b68c0ab --- /dev/null +++ b/designer-base/src/test/java/com/fr/nx/app/designer/transform/BatchTransformProgressTest.java @@ -0,0 +1,27 @@ +package com.fr.nx.app.designer.transform; + +import org.junit.Assert; +import org.junit.Test; + +/** + * Created by kerry on 2020-01-15 + */ +public class BatchTransformProgressTest { + @Test + public void testGetProgress(){ + BatchTransformProgress batchTransformProgress = new BatchTransformProgress(100); + Assert.assertEquals(0.0D, batchTransformProgress.getProgress(), 0.0D); + } + + @Test + public void testUpdateProgress(){ + BatchTransformProgress batchTransformProgress = new BatchTransformProgress(100); + for (int i = 0; i < 80; i++) { + batchTransformProgress.updateProgress(); + } + Assert.assertEquals(0.8D, batchTransformProgress.getProgress(), 0.0D); + batchTransformProgress.updateProgress(); + Assert.assertEquals(0.81D, batchTransformProgress.getProgress(), 0.0D); + + } +} diff --git a/designer-base/src/test/java/com/fr/nx/app/designer/transform/BatchTransformUtilTest.java b/designer-base/src/test/java/com/fr/nx/app/designer/transform/BatchTransformUtilTest.java new file mode 100644 index 000000000..b3adc1246 --- /dev/null +++ b/designer-base/src/test/java/com/fr/nx/app/designer/transform/BatchTransformUtilTest.java @@ -0,0 +1,35 @@ +package com.fr.nx.app.designer.transform; + +import com.fr.file.filetree.FileNode; +import org.junit.Assert; +import org.junit.Test; + +import java.util.ArrayList; +import java.util.List; + +/** + * Created by kerry on 2020-02-14 + */ +public class BatchTransformUtilTest { + @Test + public void testFilterTransformedFile(){ + FileNode[] fileNodes = new FileNode[]{ + new FileNode("test1", false), new FileNode("test2", false) + }; + List transformedFileNodes = new ArrayList(); + FileNode[] result1 = BatchTransformUtil.filterTransformedFile(fileNodes, transformedFileNodes); + Assert.assertEquals(2, result1.length); + + transformedFileNodes.add( new FileNode("test1", false)); + FileNode[] result2 = BatchTransformUtil.filterTransformedFile(fileNodes, transformedFileNodes); + Assert.assertEquals(1, result2.length); + + transformedFileNodes.add( new FileNode("test2", false)); + FileNode[] result3 = BatchTransformUtil.filterTransformedFile(fileNodes, transformedFileNodes); + Assert.assertEquals(0, result3.length); + + transformedFileNodes.add( new FileNode("test3", false)); + FileNode[] result4 = BatchTransformUtil.filterTransformedFile(fileNodes, transformedFileNodes); + Assert.assertEquals(0, result4.length); + } +} diff --git a/designer-base/src/test/java/com/fr/nx/app/designer/transform/BatchTransformerTest.java b/designer-base/src/test/java/com/fr/nx/app/designer/transform/BatchTransformerTest.java new file mode 100644 index 000000000..d29d3e742 --- /dev/null +++ b/designer-base/src/test/java/com/fr/nx/app/designer/transform/BatchTransformerTest.java @@ -0,0 +1,124 @@ +package com.fr.nx.app.designer.transform; + +import org.junit.Before; +import org.junit.Test; + +import java.util.concurrent.CountDownLatch; + +/** + * Created by kerry on 2020-01-15 + */ +// FIXME @kerry 打包失败,暂时先注释 +//@RunWith(value = PowerMockRunner.class) +//@PrepareForTest({CompileTransformUtil.class, PluginContexts.class}) +//@PowerMockIgnore({"sun.tools.attach.*", "com.sun.tools.*"}) +public class BatchTransformerTest { + + private static final int MAXPOOLSIZE = 5; + private static final int COREPOOLSIZE = 3; + private static final String THREAD_NAME_TEMPLATE = "batchtransform-thread-%s"; + + @Before + public void setUp() { +// FineRuntime.start(); +// ExecutorService threadPoolExecutor = new ThreadPoolExecutor(COREPOOLSIZE, MAXPOOLSIZE, +// 0L, TimeUnit.MILLISECONDS, +// new LinkedBlockingQueue(), +// new ThreadFactoryBuilder().setNameFormat(THREAD_NAME_TEMPLATE).build()); +// PowerMock.mockStatic(PluginContexts.class); +// PluginContext pluginContext = EasyMock.mock(PluginContext.class); +// EasyMock.expect(pluginContext.newFixedThreadPool( +// EasyMock.anyInt(), EasyMock.anyObject(ThreadFactory.class))) +// .andReturn(threadPoolExecutor).anyTimes(); +// EasyMock.expect(PluginContexts.currentContext()).andReturn(pluginContext).anyTimes(); +// +// PowerMock.mockStatic(CompileTransformUtil.class); +// TransformResultInfo resultInfo = TransformResultInfo.generateResult(TransformResult.SUCCESS); +// EasyMock.expect(CompileTransformUtil.compileFile(EasyMock.anyObject(FileNode.class))) +// .andReturn(resultInfo).anyTimes(); +// EasyMock.replay(pluginContext); +// PowerMock.replayAll(); + } + + @Test + public void testBatchTransform() { +// final CountDownLatch countDownLatch = new CountDownLatch(2); +// BatchTransformer transformer = new BatchTransformer(new MockUpdateCallBack(countDownLatch)); +// List nodeList = new ArrayList<>(); +// FileNode test1 = new FileNode("path1", false); +// FileNode test2 = new FileNode("path2", false); +// nodeList.add(test1); +// nodeList.add(test2); +// transformer.batchTransform(nodeList); +// try { +// countDownLatch.await(5, TimeUnit.SECONDS); +// } catch (InterruptedException e) { +// Assert.fail(); +// } +// Map transformResults = transformer.getResults(); +// Assert.assertEquals(2, transformResults.size()); +// Assert.assertEquals(TransformResult.SUCCESS, transformResults.get(test1).getResult()); +// Assert.assertEquals(TransformResult.SUCCESS, transformResults.get(test2).getResult()); + + } + + @Test + public void testTransform() { +// final CountDownLatch countDownLatch = new CountDownLatch(1); +// BatchTransformer transformer = new BatchTransformer(new MockUpdateCallBack(countDownLatch)); +// FileNode test = new FileNode("path", false); +// Reflect.on(transformer).call("transform", test); +// try { +// countDownLatch.await(5, TimeUnit.SECONDS); +// } catch (InterruptedException e) { +// Assert.fail(); +// } +// Map transformResults = transformer.getResults(); +// Assert.assertEquals(1, transformResults.size()); +// Assert.assertEquals(TransformResult.SUCCESS, transformResults.get(test).getResult()); + } + + @Test + public void testShutDown() { +// final CountDownLatch countDownLatch = new CountDownLatch(1); +// BatchTransformer transformer = new BatchTransformer(new MockUpdateCallBack(countDownLatch)); +// List nodeList = new ArrayList<>(); +// FileNode test = new FileNode("path", false); +// nodeList.add(test); +// transformer.batchTransform(nodeList); +// transformer.shutDown(); +// try { +// countDownLatch.await(3, TimeUnit.SECONDS); +// } catch (InterruptedException e) { +// Assert.fail(); +// } +// Map transformResults = transformer.getResults(); +// Assert.assertEquals(1, transformResults.size()); +// Assert.assertEquals(TransformResult.SUCCESS, transformResults.get(test).getResult()); + } + + + private class MockUpdateCallBack implements UpdateCallBack { + private CountDownLatch countDownLatch; + + + public MockUpdateCallBack(CountDownLatch countDownLatch) { + this.countDownLatch = countDownLatch; + } + + @Override + public void updateProgress(double progress) { + countDownLatch.countDown(); + } + + @Override + public void shutDown() { + + } + + @Override + public void reset() { + + } + } +} diff --git a/designer-base/src/test/java/com/fr/nx/app/designer/utils/CompileTransformUtilTest.java b/designer-base/src/test/java/com/fr/nx/app/designer/utils/CompileTransformUtilTest.java new file mode 100644 index 000000000..653bbdef2 --- /dev/null +++ b/designer-base/src/test/java/com/fr/nx/app/designer/utils/CompileTransformUtilTest.java @@ -0,0 +1,58 @@ +package com.fr.nx.app.designer.utils; + +import com.fr.base.FRContext; +import com.fr.base.operator.common.CommonOperator; +import com.fr.design.mainframe.JTemplate; +import com.fr.file.FILE; +import com.fr.file.filetree.FileNodes; +import com.fr.nx.app.designer.toolbar.TemplateTransformer; +import org.easymock.EasyMock; +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.powermock.api.easymock.PowerMock; +import org.powermock.core.classloader.annotations.PowerMockIgnore; +import org.powermock.core.classloader.annotations.PrepareForTest; +import org.powermock.modules.junit4.PowerMockRunner; + +/** + * Created by kerry on 2019-12-04 + */ +@RunWith(PowerMockRunner.class) +@PrepareForTest({FRContext.class}) +@PowerMockIgnore({"sun.tools.attach.*", "com.sun.tools.*"}) +public class CompileTransformUtilTest { + @Test + public void testGetTargetFile() { + CommonOperator commonOperator = EasyMock.mock(CommonOperator.class); + FileNodes fileNodes = EasyMock.mock(FileNodes.class); + EasyMock.expect(fileNodes.getSupportedTypes()).andReturn(new String[]{"cptx"}).anyTimes(); + PowerMock.mockStatic(FRContext.class); + EasyMock.expect(FRContext.getCommonOperator()).andReturn(commonOperator).anyTimes(); + EasyMock.expect(FRContext.getFileNodes()).andReturn(fileNodes).anyTimes(); + EasyMock.expect(commonOperator.getWebRootPath()).andReturn("/WebInf/").anyTimes(); + EasyMock.replay(commonOperator, fileNodes); + PowerMock.replayAll(); + JTemplate jTemplate1 = EasyMock.mock(JTemplate.class); + FILE file1 = EasyMock.mock(FILE.class); + EasyMock.expect(jTemplate1.getEditingFILE()).andReturn(file1).anyTimes(); + EasyMock.expect(file1.getPath()).andReturn("WorkBook1.cpt").anyTimes(); + EasyMock.replay(jTemplate1, file1); + FILE targetFile = CompileTransformUtil.getTargetFile(jTemplate1, TemplateTransformer.TO_CPTX); + Assert.assertEquals("WorkBook1.cptx", targetFile.getPath()); + + targetFile = CompileTransformUtil.getTargetFile(jTemplate1, TemplateTransformer.TO_CPT); + Assert.assertEquals("WorkBook1.cpt", targetFile.getPath()); + + JTemplate jTemplate2 = EasyMock.mock(JTemplate.class); + FILE file2 = EasyMock.mock(FILE.class); + EasyMock.expect(jTemplate2.getEditingFILE()).andReturn(file2).anyTimes(); + EasyMock.expect(file2.getPath()).andReturn("WorkBook1.cpt").anyTimes(); + EasyMock.replay(jTemplate2, file2); + targetFile = CompileTransformUtil.getTargetFile(jTemplate2, TemplateTransformer.TO_CPTX); + Assert.assertEquals("WorkBook1.cptx", targetFile.getPath()); + + targetFile =CompileTransformUtil.getTargetFile(jTemplate2, TemplateTransformer.TO_CPT); + Assert.assertEquals("WorkBook1.cpt", targetFile.getPath()); + } +} diff --git a/designer-chart/src/main/java/com/fr/van/chart/designer/component/VanChartTooltipContentPane.java b/designer-chart/src/main/java/com/fr/van/chart/designer/component/VanChartTooltipContentPane.java index 86086abdd..3c4cbf70c 100644 --- a/designer-chart/src/main/java/com/fr/van/chart/designer/component/VanChartTooltipContentPane.java +++ b/designer-chart/src/main/java/com/fr/van/chart/designer/component/VanChartTooltipContentPane.java @@ -47,7 +47,6 @@ import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; -import java.util.Arrays; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; @@ -464,15 +463,9 @@ public class VanChartTooltipContentPane extends BasicBeanPane params) { List tableFieldNames = VanChartRichEditorPane.getFieldNames(); - List defaultParams = Arrays.asList(getRichTextFieldNames()); - if (tableFieldNames == null || params == null) { - return; - } - - for (String fieldName : tableFieldNames) { - // 富文本默认参数和数据集字段重名时,显示默认参数 - if (!defaultParams.contains(fieldName)) { + if (tableFieldNames != null && params != null) { + for (String fieldName : tableFieldNames) { params.put(fieldName, "${" + fieldName + "_" + fieldName.hashCode() + "}"); } } diff --git a/designer-form/src/main/java/com/fr/design/actions/FormFitAttrAction.java b/designer-form/src/main/java/com/fr/design/actions/FormFitAttrAction.java new file mode 100644 index 000000000..2828abef2 --- /dev/null +++ b/designer-form/src/main/java/com/fr/design/actions/FormFitAttrAction.java @@ -0,0 +1,83 @@ +package com.fr.design.actions; + +import com.fr.design.beans.BasicBeanPane; +import com.fr.design.dialog.DialogActionAdapter; +import com.fr.design.dialog.UIDialog; +import com.fr.design.fit.DesignerUIModeConfig; +import com.fr.design.fit.NewJForm; +import com.fr.design.mainframe.DesignerContext; +import com.fr.design.mainframe.JTemplate; +import com.fr.design.menu.MenuKeySet; +import com.fr.report.fit.FitProvider; +import com.fr.report.fit.ReportFitAttr; + +import javax.swing.KeyStroke; +import java.awt.event.ActionEvent; + +/** + * Created by Administrator on 2015/7/6 0006. + */ +public class FormFitAttrAction extends JTemplateAction { + private static final MenuKeySet REPORT_FIT_ATTR_ELEMENTCASE = new MenuKeySet() { + @Override + public char getMnemonic() { + return 'T'; + } + + @Override + public String getMenuName() { + return DesignerUIModeConfig.getInstance().newUIMode() ? + com.fr.design.i18n.Toolkit.i18nText("Fine-Designer_Fit_PC_Adaptive_Attr") : + com.fr.design.i18n.Toolkit.i18nText("Fine-Designer_PC_Element_Case_Fit_Attr"); + } + + @Override + public KeyStroke getKeyStroke() { + return null; + } + }; + + public FormFitAttrAction(JTemplate jTemplate) { + super(jTemplate); + initMenuStyle(); + } + + private void initMenuStyle() { + this.setMenuKeySet(REPORT_FIT_ATTR_ELEMENTCASE); + this.setName(getMenuKeySet().getMenuKeySetName() + "..."); + this.setMnemonic(getMenuKeySet().getMnemonic()); + this.setSmallIcon(DesignerUIModeConfig.getInstance().newUIMode() ? + "/com/fr/design/images/reportfit/fit.png": + "/com/fr/design/images/reportfit/fit"); + } + + /** + * Action触发事件 + * + * @param e 事件 + */ + @Override + public void actionPerformed(ActionEvent e) { + final JTemplate jwb = getEditingComponent(); + if (jwb == null || !(jwb instanceof NewJForm)) { + return; + } + final FitProvider wbTpl = (FitProvider) jwb.getTarget(); + ReportFitAttr fitAttr = wbTpl.getReportFitAttr(); + NewJForm newJForm = (NewJForm) jwb; + BasicBeanPane attrPane = newJForm.getJFormType().obtainAttrPane(newJForm); + showFitDialog(fitAttr, jwb, wbTpl, attrPane); + } + + private void showFitDialog(ReportFitAttr fitAttr, final JTemplate jwb, final FitProvider wbTpl, final BasicBeanPane attrPane) { + attrPane.populateBean(fitAttr); + UIDialog dialog = attrPane.showMediumWindow(DesignerContext.getDesignerFrame(), new DialogActionAdapter() { + @Override + public void doOk() { + wbTpl.setReportFitAttr(attrPane.updateBean()); + jwb.fireTargetModified(); + } + }); + dialog.setVisible(true); + } +} diff --git a/designer-form/src/main/java/com/fr/design/actions/NewFormMobileAttrAction.java b/designer-form/src/main/java/com/fr/design/actions/NewFormMobileAttrAction.java new file mode 100644 index 000000000..17ec76b83 --- /dev/null +++ b/designer-form/src/main/java/com/fr/design/actions/NewFormMobileAttrAction.java @@ -0,0 +1,115 @@ +package com.fr.design.actions; + + +import com.fr.base.iofile.attr.MobileOnlyTemplateAttrMark; +import com.fr.design.designer.creator.XLayoutContainer; +import com.fr.design.designer.creator.XWAbsoluteBodyLayout; +import com.fr.design.designer.creator.XWFitLayout; +import com.fr.design.dialog.BasicDialog; +import com.fr.design.dialog.DialogActionAdapter; +import com.fr.design.fit.NewJForm; +import com.fr.design.fit.common.AdaptiveSwitchUtil; +import com.fr.design.fit.common.TemplateTool; +import com.fr.design.form.mobile.FormMobileAttrPane; +import com.fr.design.mainframe.DesignerContext; +import com.fr.design.mainframe.FormArea; +import com.fr.design.mainframe.FormDesigner; +import com.fr.design.mainframe.JForm; +import com.fr.design.mainframe.WidgetPropertyPane; +import com.fr.file.FILE; +import com.fr.form.main.Form; +import com.fr.form.main.mobile.FormMobileAttr; +import com.fr.record.analyzer.EnableMetrics; +import com.fr.stable.StringUtils; + +import java.awt.event.ActionEvent; + +/** + * Created by fanglei on 2016/11/14. + */ +@EnableMetrics +public class NewFormMobileAttrAction extends FormMobileAttrAction { + + public NewFormMobileAttrAction(JForm jf) { + super(jf); + } + + /** + * 执行动作 + * + * @return 是否执行成功 + */ + @Override + public void actionPerformed(ActionEvent e) { + final JForm jf = getEditingComponent(); + if (jf == null) { + return; + } + final Form formTpl = jf.getTarget(); + FormMobileAttr mobileAttr = formTpl.getFormMobileAttr(); + + final FormMobileAttrPane mobileAttrPane = new FormMobileAttrPane(); + mobileAttrPane.populateBean(mobileAttr); + + final boolean oldMobileOnly = mobileAttr.isMobileOnly(); + final boolean oldAdaptive = mobileAttr.isAdaptivePropertyAutoMatch(); + BasicDialog dialog = mobileAttrPane.showWindow(DesignerContext.getDesignerFrame(), new DialogActionAdapter() { + @Override + public void doOk() { + FormMobileAttr formMobileAttr = mobileAttrPane.updateBean(); + if (formMobileAttr.isMobileOnly() && jf.getTarget().getAttrMark(MobileOnlyTemplateAttrMark.XML_TAG) == null) { + // 如果是老模板,选择手机专属之后需要另存为 + FILE editingFILE = jf.getEditingFILE(); + if (editingFILE != null && editingFILE.exists()) { + String fileName = editingFILE.getName().substring(0, editingFILE.getName().length() - jf.suffix().length()) + "_mobile"; + if (!jf.saveAsTemplate(true, fileName)) { + return; + } + } + // 放到后面。如果提前 return 了,则仍然处于未设置状态,不要添加 + jf.getTarget().addAttrMark(new MobileOnlyTemplateAttrMark()); + } + // 设置移动端属性并刷新界面 + formTpl.setFormMobileAttr(formMobileAttr); // 会调整 body 的自适应布局,放到最后 + boolean changeSize = (!oldMobileOnly && formMobileAttr.isMobileOnly()) || (oldMobileOnly && !formMobileAttr.isMobileOnly()); + if (changeSize) { + ((FormArea)jf.getFormDesign().getParent()).onMobileAttrModified(); + } + //改变布局为自适应布局,只在移动端属性设置保存后改变一次 + boolean changeLayout = !oldAdaptive && formMobileAttr.isAdaptivePropertyAutoMatch(); + if (changeLayout) { + jf.getFormDesign().getSelectionModel().setSelectedCreator(jf.getFormDesign().getRootComponent()); + doChangeBodyLayout(); + WidgetPropertyPane.getInstance().refreshDockingView(); + } + jf.fireTargetModified(); + FILE editingFILE = jf.getEditingFILE(); + if(editingFILE != null && editingFILE.exists()){ + JForm jForm = getEditingComponent(); + TemplateTool.saveForm(jForm); + if (jForm instanceof NewJForm) { + AdaptiveSwitchUtil.switch2OldUI(); + } + }else { + AdaptiveSwitchUtil.switch2OldUIMode(); + NewJForm mobileJForm = new NewJForm(jf.getTarget(), jf.getEditingFILE()); + //设置临时的id,和新建的模板区分 + mobileJForm.getTarget().setTemplateID(StringUtils.EMPTY); + TemplateTool.resetTabPaneEditingTemplate(mobileJForm); + TemplateTool.activeAndResizeTemplate(mobileJForm); + } + } + }); + dialog.setVisible(true); + } + + private void doChangeBodyLayout(){ + FormDesigner formDesigner = WidgetPropertyPane.getInstance().getEditingFormDesigner(); + XLayoutContainer rootLayout = formDesigner.getRootComponent(); + if (rootLayout.getComponentCount() == 1 && rootLayout.getXCreator(0).acceptType(XWAbsoluteBodyLayout.class)) { + rootLayout = (XWAbsoluteBodyLayout) rootLayout.getXCreator(0); + } + ((XWFitLayout)formDesigner.getRootComponent()).switch2FitBodyLayout(rootLayout); + } +} + diff --git a/designer-form/src/main/java/com/fr/design/designer/beans/LayoutAdapter.java b/designer-form/src/main/java/com/fr/design/designer/beans/LayoutAdapter.java index e2aa7f152..f10b877e2 100644 --- a/designer-form/src/main/java/com/fr/design/designer/beans/LayoutAdapter.java +++ b/designer-form/src/main/java/com/fr/design/designer/beans/LayoutAdapter.java @@ -54,7 +54,7 @@ public interface LayoutAdapter { * @return 是否添加成功,成功返回true,否则false */ boolean addBean(XCreator creator, int x, int y, boolean accept); - + /** * 返回该布局管理适配器的Painter,为容器提供放置位置的标识。 */ diff --git a/designer-form/src/main/java/com/fr/design/designer/creator/XChartEditor.java b/designer-form/src/main/java/com/fr/design/designer/creator/XChartEditor.java index a6a29339e..e316bf2b6 100644 --- a/designer-form/src/main/java/com/fr/design/designer/creator/XChartEditor.java +++ b/designer-form/src/main/java/com/fr/design/designer/creator/XChartEditor.java @@ -267,6 +267,9 @@ public class XChartEditor extends XBorderStyleWidgetCreator { if (editingMouseListener.stopEditing()) { if (this != (XCreator) designer.getRootComponent()) { ComponentAdapter adapter = AdapterBus.getComponentAdapter(designer, this); + if (isEditing) { + EastRegionContainerPane.getInstance().switchTabTo(EastRegionContainerPane.KEY_WIDGET_SETTINGS); + } editingMouseListener.startEditing(this, isEditing ? adapter.getDesignerEditor() : null, adapter); if (isEditing) { EastRegionContainerPane.getInstance().switchTabTo(EastRegionContainerPane.KEY_WIDGET_SETTINGS); diff --git a/designer-form/src/main/java/com/fr/design/designer/creator/XWAbsoluteLayout.java b/designer-form/src/main/java/com/fr/design/designer/creator/XWAbsoluteLayout.java index da268b0e6..86d9366de 100644 --- a/designer-form/src/main/java/com/fr/design/designer/creator/XWAbsoluteLayout.java +++ b/designer-form/src/main/java/com/fr/design/designer/creator/XWAbsoluteLayout.java @@ -14,6 +14,7 @@ import com.fr.design.designer.beans.location.Direction; import com.fr.design.designer.beans.models.SelectionModel; import com.fr.design.designer.creator.cardlayout.XWTabFitLayout; import com.fr.design.designer.properties.mobile.MobileBooKMarkUsePropertyUI; +import com.fr.design.fit.DesignerUIModeConfig; import com.fr.design.form.layout.FRAbsoluteLayout; import com.fr.design.form.util.FormDesignerUtils; import com.fr.design.form.util.XCreatorConstants; @@ -30,7 +31,6 @@ import com.fr.form.ui.Widget; import com.fr.form.ui.container.WAbsoluteLayout; import com.fr.form.ui.container.WAbsoluteLayout.BoundsWidget; import com.fr.form.ui.container.WLayout; -import com.fr.general.FRScreen; import com.fr.general.IOUtils; import com.fr.share.ShareConstants; @@ -111,7 +111,7 @@ public class XWAbsoluteLayout extends XLayoutContainer { private void initPercent(WAbsoluteLayout widget) { Toolkit toolkit = Toolkit.getDefaultToolkit(); Dimension scrnsize = toolkit.getScreenSize(); - double screenValue = FRScreen.getDesignScreenByDimension(scrnsize).getValue(); + double screenValue = DesignerUIModeConfig.getInstance().getDesignScreenByDimension(scrnsize).getValue(); if (!AssistUtils.equals(screenValue, FormArea.DEFAULT_SLIDER)) { this.setContainerPercent(screenValue / FormArea.DEFAULT_SLIDER); } @@ -247,7 +247,7 @@ public class XWAbsoluteLayout extends XLayoutContainer { */ @Override public void updateChildBound(int minHeight) { - double prevContainerPercent = FRScreen.getDesignScreenByDimension(toData().getDesigningResolution()).getValue() / FormArea.DEFAULT_SLIDER; + double prevContainerPercent = DesignerUIModeConfig.getInstance().getDesignScreenByDimension(toData().getDesigningResolution()).getValue() / FormArea.DEFAULT_SLIDER; if (toData().getCompState() == 0 && !AssistUtils.equals(containerPercent, prevContainerPercent)) { for (int i = 0; i < this.getComponentCount(); i++) { XCreator creator = getXCreator(i); diff --git a/designer-form/src/main/java/com/fr/design/designer/creator/XWFitLayout.java b/designer-form/src/main/java/com/fr/design/designer/creator/XWFitLayout.java index 6d55c658f..450dfeae3 100644 --- a/designer-form/src/main/java/com/fr/design/designer/creator/XWFitLayout.java +++ b/designer-form/src/main/java/com/fr/design/designer/creator/XWFitLayout.java @@ -6,6 +6,7 @@ import com.fr.design.designer.beans.location.Direction; import com.fr.design.designer.creator.cardlayout.XWCardMainBorderLayout; import com.fr.design.designer.creator.cardlayout.XWTabFitLayout; import com.fr.design.designer.properties.mobile.BodyMobilePropertyUI; +import com.fr.design.fit.DesignerUIModeConfig; import com.fr.design.form.layout.FRFitLayout; import com.fr.design.fun.WidgetPropertyUIProvider; import com.fr.design.mainframe.FormArea; @@ -20,7 +21,6 @@ import com.fr.form.ui.container.WFitLayout; import com.fr.form.ui.container.WLayout; import com.fr.general.ComparatorUtils; import com.fr.general.FRLogger; -import com.fr.general.FRScreen; import com.fr.stable.ArrayUtils; import com.fr.stable.AssistUtils; import javax.swing.JOptionPane; @@ -72,7 +72,7 @@ public class XWFitLayout extends XLayoutContainer { private void initPercent(){ Toolkit toolkit = Toolkit.getDefaultToolkit(); Dimension scrnsize = toolkit.getScreenSize(); - double screenValue = FRScreen.getDesignScreenByDimension(scrnsize).getValue(); + double screenValue = DesignerUIModeConfig.getInstance().getDesignScreenByDimension(scrnsize).getValue(); if (!AssistUtils.equals(FormArea.DEFAULT_SLIDER, screenValue)) { this.setContainerPercent(screenValue / FormArea.DEFAULT_SLIDER); } diff --git a/designer-form/src/main/java/com/fr/design/fit/DesignerUIModeConfig.java b/designer-form/src/main/java/com/fr/design/fit/DesignerUIModeConfig.java new file mode 100644 index 000000000..5e743d383 --- /dev/null +++ b/designer-form/src/main/java/com/fr/design/fit/DesignerUIModeConfig.java @@ -0,0 +1,124 @@ +package com.fr.design.fit; + +import com.fr.base.ScreenResolution; +import com.fr.design.fun.ReportLengthUNITProvider; +import com.fr.design.unit.UnitConvertUtil; +import com.fr.general.ComparatorUtils; +import com.fr.general.FRScreen; +import com.fr.stable.Constants; + +import java.awt.Dimension; + +/** + * Created by kerry on 2020-06-05 + */ +public class DesignerUIModeConfig { + private DesignerUIMode mode = DesignerUIMode.NEW_UI_MODE; + + private static class DesignerUIModeConfigHolder { + private static final DesignerUIModeConfig designerUIModeConfig = new DesignerUIModeConfig(); + } + + private DesignerUIModeConfig() { + + } + + public static DesignerUIModeConfig getInstance() { + return DesignerUIModeConfigHolder.designerUIModeConfig; + } + + + /** + * 判断是否是新ui模式 + * + * @return boolean + */ + public boolean newUIMode() { + return ComparatorUtils.equals(DesignerUIMode.NEW_UI_MODE, mode); + } + + /** + * 设置新ui模式 + */ + public void setNewUIMode() { + this.mode = DesignerUIMode.NEW_UI_MODE; + } + + /** + * 设置老ui模式 + */ + public void setOldUIMode() { + this.mode = DesignerUIMode.OLD_UI_MODE; + } + + /** + * 解析不同模式下的尺寸单位 + * + * @param unitType 尺寸类型 + * @return ReportLengthUNITProvider对象 + */ + public ReportLengthUNITProvider parseLengthUNIT(int unitType) { + return mode.parseLengthUNIT(unitType); + } + + /** + * 获取不同模式下的屏幕分辨率 + * + * @return 分辨率 + */ + public int getScreenResolution() { + return mode.getScreenResolution(); + } + + /** + * 根据屏幕尺寸获取设计时的FRScreen + * + * @param screen 屏幕尺寸 + * @return FRScreen + */ + public FRScreen getDesignScreenByDimension(Dimension screen) { + return mode.getDesignScreenByDimension(screen); + } + + private enum DesignerUIMode { + OLD_UI_MODE { + @Override + protected ReportLengthUNITProvider parseLengthUNIT(int unitType) { + return UnitConvertUtil.parseLengthUNIT(unitType); + } + + @Override + protected FRScreen getDesignScreenByDimension(Dimension screen) { + return FRScreen.getDesignScreenByDimension(screen); + } + + @Override + protected int getScreenResolution() { + return ScreenResolution.getScreenResolution(); + } + }, + NEW_UI_MODE { + @Override + protected ReportLengthUNITProvider parseLengthUNIT(int unitType) { + return new PXReportLengthUNIT(); + } + + @Override + protected FRScreen getDesignScreenByDimension(Dimension screen) { + return FRScreen.p1440; + } + + @Override + protected int getScreenResolution() { + return Constants.DEFAULT_WEBWRITE_AND_SCREEN_RESOLUTION; + } + }; + + protected abstract ReportLengthUNITProvider parseLengthUNIT(int unitType); + + protected abstract FRScreen getDesignScreenByDimension(Dimension screen); + + protected abstract int getScreenResolution(); + } + +} diff --git a/designer-form/src/main/java/com/fr/design/fit/FitStateCompatible.java b/designer-form/src/main/java/com/fr/design/fit/FitStateCompatible.java new file mode 100644 index 000000000..06934e98e --- /dev/null +++ b/designer-form/src/main/java/com/fr/design/fit/FitStateCompatible.java @@ -0,0 +1,36 @@ +package com.fr.design.fit; + +/** + * Created by kerry on 2020-04-27 + */ +public enum FitStateCompatible { + DOUBLE_FIT(2, 0), + HOR_FIT(1, 1), + NO_FIT(3, 2); + + private final int newType; + private final int oldType; + + FitStateCompatible(int oldType, int newType) { + this.oldType = oldType; + this.newType = newType; + } + + public static int getNewTypeFromOld(int oldValue) { + for (FitStateCompatible compatible : values()) { + if (compatible.oldType == oldValue) { + return compatible.newType; + } + } + return DOUBLE_FIT.newType; + } + + public static int getOldTypeFromNew(int newValue) { + for (FitStateCompatible compatible : values()) { + if (compatible.newType == newValue) { + return compatible.oldType; + } + } + return DOUBLE_FIT.oldType; + } +} diff --git a/designer-form/src/main/java/com/fr/design/fit/JFormType.java b/designer-form/src/main/java/com/fr/design/fit/JFormType.java new file mode 100644 index 000000000..7fe795c6f --- /dev/null +++ b/designer-form/src/main/java/com/fr/design/fit/JFormType.java @@ -0,0 +1,133 @@ +package com.fr.design.fit; + +import com.fr.design.beans.BasicBeanPane; +import com.fr.design.fit.common.AdaptiveSwitchUtil; +import com.fr.form.fit.NewFormMarkAttr; +import com.fr.form.fit.config.FormFitConfig; +import com.fr.design.fit.menupane.FormFitAttrPane; +import com.fr.design.fun.PreviewProvider; +import com.fr.design.mainframe.JTemplate; +import com.fr.design.preview.FormAdaptivePreview; +import com.fr.design.preview.FormPreview; +import com.fr.design.report.fit.menupane.ReportFitAttrPane; +import com.fr.form.main.Form; +import com.fr.report.fit.ReportFitAttr; + +public enum JFormType { + OLD_TYPE(0, new FormPreview()) { + @Override + public void switchUI() { + AdaptiveSwitchUtil.switch2OldUI(); + } + + @Override + public void switchUIMode() { + AdaptiveSwitchUtil.switch2OldUIMode(); + } + + @Override + public ReportFitAttr obtainFitAttr() { + return FormFitConfig.getInstance().getOldFitAttr(); + } + + @Override + public void updateFitAttr(ReportFitAttr attr) { + FormFitConfig.getInstance().setOldFitAttr(attr); + } + + @Override + public BasicBeanPane obtainAttrPane(NewJForm newJForm) { + return new ReportFitAttrPane(); + } + + }, + NEW_TYPE(1, new FormAdaptivePreview()) { + @Override + public void switchUI() { + AdaptiveSwitchUtil.switch2NewUI(); + } + + @Override + public void switchUIMode() { + AdaptiveSwitchUtil.switch2NewUIMode(); + } + + @Override + public ReportFitAttr obtainFitAttr() { + return FormFitConfig.getInstance().getNewFitAttr(); + } + + @Override + public void updateFitAttr(ReportFitAttr attr) { + FormFitConfig.getInstance().setNewFitAttr(attr); + } + + @Override + public BasicBeanPane obtainAttrPane(NewJForm newJForm) { + return new FormFitAttrPane(newJForm); + } + }; + private int type; + private boolean newType; + private PreviewProvider previewType; + + JFormType(int type, PreviewProvider previewType) { + this.type = type; + this.newType = (type == 1); + this.previewType = previewType; + } + + public int getType() { + return type; + } + + public boolean isNewType() { + return newType; + } + + public PreviewProvider getPreviewType() { + return previewType; + } + + public abstract void switchUI(); + + public abstract void switchUIMode(); + + public abstract ReportFitAttr obtainFitAttr(); + + public abstract void updateFitAttr(ReportFitAttr attr); + + public abstract BasicBeanPane obtainAttrPane(NewJForm newJForm); + + /** + * @Description: 更新模板的标志位 + * @param jTemplate + * @return: + * @Author: Henry.Wang + * @date: 2020/12/17 16:17 + */ + public void updateJFromTemplateType(JTemplate jTemplate) { + if (jTemplate instanceof NewJForm) { + NewJForm newJForm = (NewJForm) jTemplate; + Form form = newJForm.getTarget(); + NewFormMarkAttr newFormMarkAttr = form.getAttrMark(NewFormMarkAttr.XML_TAG); + if (newFormMarkAttr == null) { + newFormMarkAttr = new NewFormMarkAttr(this.getType()); + form.addAttrMark(newFormMarkAttr); + } + newFormMarkAttr.setType(this.getType()); + newJForm.setJFormType(this); + } + } + + /** + * @Description: 更新预览方式 + * @param jTemplate + * @return: + * @Author: Henry.Wang + * @date: 2020/12/17 16:17 + */ + public void updatePreviewType(JTemplate jTemplate) { + jTemplate.setPreviewType(this.getPreviewType()); + } +} diff --git a/designer-form/src/main/java/com/fr/design/fit/NewJForm.java b/designer-form/src/main/java/com/fr/design/fit/NewJForm.java new file mode 100644 index 000000000..4df8b4386 --- /dev/null +++ b/designer-form/src/main/java/com/fr/design/fit/NewJForm.java @@ -0,0 +1,285 @@ +package com.fr.design.fit; + +import com.fr.base.DynamicUnitList; +import com.fr.design.actions.TemplateParameterAction; +import com.fr.design.designer.beans.AdapterBus; +import com.fr.design.designer.beans.LayoutAdapter; +import com.fr.design.designer.beans.adapters.layout.FRFitLayoutAdapter; +import com.fr.design.designer.beans.events.DesignerEditListener; +import com.fr.design.designer.beans.events.DesignerEvent; +import com.fr.design.designer.creator.*; +import com.fr.design.fit.common.AdaptiveSwitchUtil; +import com.fr.design.fit.common.LayoutTool; +import com.fr.form.fit.NewFormMarkAttr; +import com.fr.design.fit.common.TemplateTool; +import com.fr.design.actions.FormFitAttrAction; +import com.fr.design.actions.NewFormMobileAttrAction; +import com.fr.design.gui.ibutton.UIButton; +import com.fr.design.preview.DeveloperPreview; +import com.fr.design.preview.FormAdaptivePreview; +import com.fr.design.fit.toolbar.SwitchAction; +import com.fr.design.fun.PreviewProvider; +import com.fr.design.mainframe.FormDesigner; +import com.fr.design.mainframe.JForm; +import com.fr.design.mainframe.WidgetPropertyPane; +import com.fr.design.menu.ShortCut; +import com.fr.design.preview.FormPreview; +import com.fr.design.preview.MobilePreview; +import com.fr.design.utils.ComponentUtils; +import com.fr.file.FILE; +import com.fr.form.FormElementCaseProvider; +import com.fr.form.main.Form; +import com.fr.form.main.mobile.FormMobileAttr; +import com.fr.form.ui.ElementCaseEditor; +import com.fr.stable.ArrayUtils; + +import javax.swing.JComponent; +import java.awt.Dimension; +import java.awt.Rectangle; + + +/** + * Created by kerry on 2020-05-31 + */ +public class NewJForm extends JForm { + private static final int FUZZY_EDGE = 10; + private static final int TITLE_HEIGHT = 36; + private JFormType jFormType; + + public NewJForm() { + super(); + init(); + changePaneSize(); + } + + public NewJForm(Form form, FILE file) { + super(form, file); + if (DesignerUIModeConfig.getInstance().newUIMode()) { + init(); + } + changePaneSize(); + } + + /** + * @Description:改变body的大小,主要针对新模式下打开老模板出现截断的情况 + * @param + * @return: + * @Author: Henry.Wang + * @date: 2020/9/13 23:23 + */ + private void changePaneSize() { + if (mobileForm()) + return; + NewFormMarkAttr newFormMarkAttr = this.getTarget().getAttrMark(NewFormMarkAttr.XML_TAG); + if (newFormMarkAttr.isNotSetOriginSize()) { + newFormMarkAttr.setBodyHeight(LayoutTool.getBodyHeight(this)); + newFormMarkAttr.setBodyWidth(LayoutTool.getBodyWidth(this)); + return; + } + //这种是针对body为绝对布局时,可能出现截断的情况 + if (LayoutTool.absoluteLayoutForm(this)) { + int bodyHeight = newFormMarkAttr.getBodyHeight(); + int bodyWidth = newFormMarkAttr.getBodyWidth(); + Rectangle rectangle = LayoutTool.getAbsoluteBodySize(this); + if (!isNewJFrom() && (rectangle.width != bodyWidth || rectangle.height != bodyHeight)) { + TemplateTool.onlyChangeAbsoluteBodySize(bodyHeight, bodyWidth, this); + } else if(isNewJFrom()){ + if (rectangle.width > bodyWidth && rectangle.height > bodyHeight) { + TemplateTool.onlyChangeAbsoluteBodySize(rectangle.height, rectangle.width, this); + } else if (rectangle.width > bodyWidth) { + TemplateTool.onlyChangeAbsoluteBodySize(bodyHeight, rectangle.width, this); + } else if (rectangle.height > bodyHeight) { + TemplateTool.onlyChangeAbsoluteBodySize(rectangle.height, bodyWidth, this); + } + } + } else if (AdaptiveSwitchUtil.isSwitchJFromIng()) { + //这种是针对body中有绝对画布块,导致截断的情况 + double scale = LayoutTool.getContainerPercent(); + if (isNewJFrom()) { + LayoutTool.scaleAbsoluteBlockComponentsBounds(this.getFormDesign().getRootComponent(), 1 / scale); + } else { + LayoutTool.scaleAbsoluteBlockComponentsBounds(this.getFormDesign().getRootComponent(), scale); + } + } + } + + public JFormType getJFormType() { + return this.jFormType; + } + + public void setJFormType(JFormType jFormType) { + this.jFormType = jFormType; + } + + + /** + * 模板菜单 + * + * @return 返回菜单 + */ + @Override + public ShortCut[] shortcut4TemplateMenu() { + if (this.index == FORM_TAB) { + return ArrayUtils.addAll(new ShortCut[]{new TemplateParameterAction(this), new NewFormMobileAttrAction(this), new FormFitAttrAction(this)}, new ShortCut[0]); + } else { + return ArrayUtils.addAll(new ShortCut[]{new TemplateParameterAction(this), new NewFormMobileAttrAction(this), new FormFitAttrAction(this)}, this.getElementCaseDesign().shortcut4TemplateMenu()); + } + } + + + private void init() { + this.getFormDesign().addDesignerEditListener(new DesignerEditListener() { + private Rectangle oldRec; + + @Override + public void fireCreatorModified(DesignerEvent evt) { + if (evt.getAffectedCreator() == null) { + return; + } + if (evt.getCreatorEventID() == DesignerEvent.CREATOR_RESIZED) { + XComponent affectedCreator = evt.getAffectedCreator(); + if (isElementCase((JComponent) affectedCreator)) { + processAbsorbingEffect((XWTitleLayout) affectedCreator); + } + } + if (evt.getCreatorEventID() == DesignerEvent.CREATOR_SELECTED) { + XComponent affectedCreator = evt.getAffectedCreator(); + oldRec = new Rectangle(affectedCreator.getBounds()); + } + } + + private boolean isElementCase(JComponent component) { + return component.getComponents().length > 0 && component.getComponent(0) instanceof XElementCase; + } + + private void processAbsorbingEffect(XWTitleLayout xwTitleLayout) { + NewJForm.this.getFormDesign().repaint(); + Dimension bound = new Dimension(xwTitleLayout.getSize()); + ElementCaseEditor elementCaseEditor = (ElementCaseEditor) xwTitleLayout.toData().getBodyBoundsWidget().getWidget(); + FormElementCaseProvider elementCase = elementCaseEditor.getElementCase(); + if (oldRec.width != bound.width) { + processColumnAbsorbingEffect(xwTitleLayout, elementCase.getColumnWidthList_DEC(), bound); + oldRec.width = bound.width; + } + if (oldRec.height != bound.height) { + processRowAbsorbingEffect(xwTitleLayout, elementCase.getRowHeightList_DEC(), bound); + oldRec.height = bound.height; + } + NewJForm.this.getFormDesign().refreshDesignerUI(); + } + + private boolean hasTitle(XWTitleLayout xwTitleLayout) { + return xwTitleLayout.getComponentCount() > 1; + } + + private void processColumnAbsorbingEffect(XWTitleLayout xwTitleLayout, DynamicUnitList columnUnitList, Dimension bound) { + int temp = 0; + int resolution = DesignerUIModeConfig.getInstance().getScreenResolution();; + int difference = 0; + int i = 0; + while (true) { + if (i < columnUnitList.size()) { + temp += columnUnitList.get(i++).toPixI(resolution); + } else { + //在处理吸附效果时,columnUnitList数组长度可能比实际显示的列数少,所以用默认值来补充 + temp += columnUnitList.getDefaultUnit().toPixI(resolution); + } + if (bound.width - temp < 0) { + break; + } + if (bound.width - temp < FUZZY_EDGE) { + difference = bound.width - temp; + bound.width = temp; + break; + } + } + fixLayout(xwTitleLayout, bound, difference, 0); + } + + private void processRowAbsorbingEffect(XWTitleLayout xwTitleLayout, DynamicUnitList rowUnitList, Dimension bound) { + int temp = hasTitle(xwTitleLayout) ? TITLE_HEIGHT : 0; + int resolution =DesignerUIModeConfig.getInstance().getScreenResolution(); + int difference = 0; + int i = 0; + while (true) { + if (i < rowUnitList.size()) { + temp += rowUnitList.get(i++).toPixI(resolution); + } else { + //在处理吸附效果时,rowUnitList数组长度可能比实际显示的行数少,所以用默认值来补充 + temp += rowUnitList.getDefaultUnit().toPixI(resolution); + } + if (bound.height - temp < 0) { + break; + } + if (bound.height - temp < FUZZY_EDGE) { + difference = bound.height - temp; + bound.height = temp; + break; + } + } + fixLayout(xwTitleLayout, bound, difference, 1); + } + + + private void fixLayout(XWTitleLayout xwTitleLayout, Dimension bound, int difference, int row) { + Rectangle backupBounds = getBound(xwTitleLayout); + xwTitleLayout.setSize(bound); + FormDesigner formDesigner = WidgetPropertyPane.getInstance().getEditingFormDesigner(); + LayoutAdapter adapter = AdapterBus.searchLayoutAdapter(formDesigner, xwTitleLayout); + if (adapter instanceof FRFitLayoutAdapter) { + FRFitLayoutAdapter layoutAdapter = (FRFitLayoutAdapter) adapter; + layoutAdapter.setEdit(true); + layoutAdapter.calculateBounds(backupBounds, xwTitleLayout.getBounds(), xwTitleLayout, row, difference); + } + XLayoutContainer parent = XCreatorUtils.getParentXLayoutContainer(xwTitleLayout); + if (parent != null && parent.toData() != null) { + parent.toData().setBounds(xwTitleLayout.toData(), xwTitleLayout.getBounds()); + } + + } + + }); + } + + + public Rectangle getBound(XWTitleLayout creator) { + Rectangle bounds = new Rectangle(creator.getBounds()); + if (creator.getParent() == null) { + return bounds; + } + Rectangle rec = ComponentUtils.getRelativeBounds(creator.getParent()); + bounds.x += rec.x; + bounds.y += rec.y; + return bounds; + + } + + @Override + public PreviewProvider[] supportPreview() { + if (isNewJFrom()) { + return new PreviewProvider[]{new FormAdaptivePreview(), new DeveloperPreview(), new MobilePreview()}; + } + return new PreviewProvider[]{new FormPreview(), new MobilePreview()}; + } + + public boolean mobileForm() { + FormMobileAttr mobileAttr = this.getTarget().getFormMobileAttr(); + if (mobileAttr.isMobileOnly() && mobileAttr.isAdaptivePropertyAutoMatch()) { + return true; + } + return false; + } + + private SwitchAction switchAction; + + public UIButton[] createExtraButtons() { + UIButton[] extraButtons = super.createExtraButtons(); + switchAction = new SwitchAction(); + return ArrayUtils.addAll(extraButtons, new UIButton[]{switchAction.getToolBarButton()}); + } + + + public boolean isNewJFrom() { + return jFormType == null || jFormType.isNewType(); + } +} diff --git a/designer-form/src/main/java/com/fr/design/fit/PX.java b/designer-form/src/main/java/com/fr/design/fit/PX.java new file mode 100644 index 000000000..720268ea6 --- /dev/null +++ b/designer-form/src/main/java/com/fr/design/fit/PX.java @@ -0,0 +1,27 @@ +package com.fr.design.fit; + +import com.fr.stable.Constants; +import com.fr.stable.unit.LEN_UNIT; + +public class PX extends LEN_UNIT { + private static final long FU_SCALE = 36576L; + + public PX(float var1) { + super(var1); + } + + + public boolean equals(Object var1) { + return var1 instanceof PX && super.equals(var1); + } + + protected long getMoreFUScale() { + int dpi = DesignerUIModeConfig.getInstance().getScreenResolution(); + return FU_SCALE / dpi; + } + + public static double toPixWithResolution(double value, int resolution) { + return value * resolution / Constants.FR_PAINT_RESOLUTION; + } + +} diff --git a/designer-form/src/main/java/com/fr/design/fit/PXReportLengthUNIT.java b/designer-form/src/main/java/com/fr/design/fit/PXReportLengthUNIT.java new file mode 100644 index 000000000..8d8f1de16 --- /dev/null +++ b/designer-form/src/main/java/com/fr/design/fit/PXReportLengthUNIT.java @@ -0,0 +1,32 @@ +package com.fr.design.fit; + +import com.fr.design.fun.impl.AbstractReportLengthUNITProvider; +import com.fr.stable.unit.UNIT; + +/** + * Created by kerry on 2020-04-09 + */ +public class PXReportLengthUNIT extends AbstractReportLengthUNITProvider { + public static final short UNIT_TYPE = 4; + + @Override + public String unitText() { + return com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Basic_Px"); + } + + @Override + public int unitType() { + return UNIT_TYPE; + } + + @Override + public float unit2Value4Scale(UNIT value) { + int resolution = DesignerUIModeConfig.getInstance().getScreenResolution(); + return value.toPixF(resolution); + } + + @Override + public UNIT float2UNIT(float value) { + return new PX(value); + } +} diff --git a/designer-form/src/main/java/com/fr/design/fit/attrpane/ElementEditorExtendDefinePane.java b/designer-form/src/main/java/com/fr/design/fit/attrpane/ElementEditorExtendDefinePane.java new file mode 100644 index 000000000..3d25590f4 --- /dev/null +++ b/designer-form/src/main/java/com/fr/design/fit/attrpane/ElementEditorExtendDefinePane.java @@ -0,0 +1,91 @@ +package com.fr.design.fit.attrpane; + +import com.fr.design.designer.IntervalConstants; +import com.fr.design.designer.creator.CRPropertyDescriptor; +import com.fr.design.designer.creator.PropertyGroupPane; +import com.fr.design.designer.creator.XCreator; +import com.fr.design.designer.creator.XElementCase; +import com.fr.design.gui.ilable.UILabel; +import com.fr.design.layout.FRGUIPaneFactory; +import com.fr.design.layout.TableLayoutHelper; +import com.fr.design.mainframe.widget.accessibles.AccessibleElementCaseToolBarEditor; +import com.fr.design.widget.ui.designer.component.PaddingBoundPane; +import com.fr.design.widget.ui.designer.layout.WTitleLayoutDefinePane; +import com.fr.form.ui.ElementCaseEditor; +import com.fr.form.web.FormToolBarManager; +import com.fr.general.ComparatorUtils; +import com.fr.report.fit.ReportFitAttr; + +import javax.swing.BorderFactory; +import javax.swing.JPanel; +import java.awt.BorderLayout; +import java.awt.Component; +import java.util.ArrayList; +import java.util.List; + +/** + * Created by kerry on 2020-04-09 + */ +public class ElementEditorExtendDefinePane extends WTitleLayoutDefinePane { + private PaddingBoundPane paddingBoundPane; + private AccessibleElementCaseToolBarEditor elementCaseToolBarEditor; + private PropertyGroupPane extraPropertyGroupPane; + private PcFitExpandablePane pcFitExpandablePane; + + private static final String FIT_STATE_PC = "fitStateInPC"; + + public ElementEditorExtendDefinePane(XCreator xCreator) { + super(xCreator); + } + + protected JPanel createCenterPane() { + JPanel centerPane = FRGUIPaneFactory.createBorderLayout_S_Pane(); + paddingBoundPane = new PaddingBoundPane(); + elementCaseToolBarEditor = new AccessibleElementCaseToolBarEditor(); + Component[][] components = new Component[][]{ + new Component[]{paddingBoundPane, null}, + new Component[]{new UILabel(com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Form_EC_Toolbar")), elementCaseToolBarEditor}, + }; + JPanel panel = TableLayoutHelper.createGapTableLayoutPane(components, TableLayoutHelper.FILL_LASTCOLUMN, IntervalConstants.INTERVAL_W0, IntervalConstants.INTERVAL_L1); + panel.setBorder(BorderFactory.createEmptyBorder(5, 0, 10, 0)); + CRPropertyDescriptor[] extraTableEditor = removeOldFitEditor(((XElementCase) creator).getExtraTableEditor()); + extraPropertyGroupPane = new PropertyGroupPane(extraTableEditor, creator); + centerPane.add(panel, BorderLayout.NORTH); + if (extraTableEditor.length > 0) { + centerPane.add(extraPropertyGroupPane, BorderLayout.CENTER); + } + pcFitExpandablePane = new PcFitExpandablePane(this); + centerPane.add(pcFitExpandablePane, BorderLayout.SOUTH); + return centerPane; + } + + private CRPropertyDescriptor[] removeOldFitEditor(CRPropertyDescriptor[] extraTableEditor) { + List list = new ArrayList(); + for (CRPropertyDescriptor crPropertyDescriptor : extraTableEditor) { + if (!ComparatorUtils.equals(FIT_STATE_PC, crPropertyDescriptor.getName())) { + list.add(crPropertyDescriptor); + } + } + return list.toArray(new CRPropertyDescriptor[list.size()]); + } + + protected ElementCaseEditor updateSubBean() { + ElementCaseEditor elementCaseEditor = (ElementCaseEditor) creator.toData(); + if (ComparatorUtils.equals(getGlobalName(), com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Form_Layout_Padding_Duplicate"))) { + paddingBoundPane.update(elementCaseEditor); + } + elementCaseEditor.setToolBars((FormToolBarManager[]) elementCaseToolBarEditor.getValue()); + ReportFitAttr fitAttr = pcFitExpandablePane.update(); + elementCaseEditor.setReportFitAttr(fitAttr); + return elementCaseEditor; + } + + + protected void populateSubBean(ElementCaseEditor ob) { + paddingBoundPane.populate(ob); + elementCaseToolBarEditor.setValue(ob.getToolBars()); + extraPropertyGroupPane.populate(ob); + pcFitExpandablePane.populate(ob.getReportFitAttr()); + + } +} diff --git a/designer-form/src/main/java/com/fr/design/fit/attrpane/FRAbsoluteBodyLayoutExtendDefinePane.java b/designer-form/src/main/java/com/fr/design/fit/attrpane/FRAbsoluteBodyLayoutExtendDefinePane.java new file mode 100644 index 000000000..69781e7a6 --- /dev/null +++ b/designer-form/src/main/java/com/fr/design/fit/attrpane/FRAbsoluteBodyLayoutExtendDefinePane.java @@ -0,0 +1,161 @@ +package com.fr.design.fit.attrpane; + +import com.fr.base.io.IOFile; +import com.fr.base.iofile.attr.WatermarkAttr; +import com.fr.design.data.DataCreatorUI; +import com.fr.design.designer.IntervalConstants; +import com.fr.design.designer.creator.XCreator; +import com.fr.design.designer.creator.XWFitLayout; +import com.fr.design.designer.properties.items.FRLayoutTypeItems; +import com.fr.design.designer.properties.items.Item; +import com.fr.design.foldablepane.UIExpandablePane; +import com.fr.design.gui.icombobox.UIComboBox; +import com.fr.design.gui.ilable.UILabel; +import com.fr.design.layout.FRGUIPaneFactory; +import com.fr.design.layout.TableLayout; +import com.fr.design.layout.TableLayoutHelper; +import com.fr.design.mainframe.WidgetPropertyPane; +import com.fr.design.mainframe.widget.accessibles.AccessibleBodyWatermarkEditor; +import com.fr.design.mainframe.widget.accessibles.AccessibleWLayoutBorderStyleEditor; +import com.fr.design.utils.gui.UIComponentUtils; +import com.fr.design.widget.FRWidgetFactory; +import com.fr.design.widget.ui.designer.layout.FRAbsoluteLayoutDefinePane; +import com.fr.form.ui.LayoutBorderStyle; +import com.fr.form.ui.container.WAbsoluteBodyLayout; +import com.fr.form.ui.container.WAbsoluteLayout; +import com.fr.form.ui.container.WBodyLayoutType; +import com.fr.report.core.ReportUtils; + +import javax.swing.BorderFactory; +import javax.swing.DefaultComboBoxModel; +import javax.swing.JPanel; +import java.awt.BorderLayout; +import java.awt.Component; + +/** + * Created by kerry on 2020-04-22 + */ +public class FRAbsoluteBodyLayoutExtendDefinePane extends FRAbsoluteLayoutDefinePane { + private static final int MAX_LABEL_WIDTH = 80; + + private AccessibleWLayoutBorderStyleEditor borderStyleEditor; + private AccessibleBodyWatermarkEditor watermarkEditor; + + private UIComboBox layoutCombox; + private WBodyLayoutType layoutType = WBodyLayoutType.ABSOLUTE; + + public FRAbsoluteBodyLayoutExtendDefinePane(XCreator xCreator) { + super(xCreator); + } + + + public void initComponent() { + initCenterPane(); + JPanel centerPane = FRGUIPaneFactory.createBorderLayout_S_Pane(); + borderStyleEditor = new AccessibleWLayoutBorderStyleEditor(); + watermarkEditor = new AccessibleBodyWatermarkEditor(); + JPanel jPanel = TableLayoutHelper.createGapTableLayoutPane( + new Component[][]{ + new Component[]{new UILabel(com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Form_Widget_Style")), borderStyleEditor}, + new Component[]{new UILabel(com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Form_WaterMark")), watermarkEditor} + }, TableLayoutHelper.FILL_LASTCOLUMN, IntervalConstants.INTERVAL_W3, IntervalConstants.INTERVAL_L1); + JPanel borderPane = FRGUIPaneFactory.createBorderLayout_S_Pane(); + jPanel.setBorder(BorderFactory.createEmptyBorder(10, 0, 10, 0)); + borderPane.add(jPanel, BorderLayout.CENTER); + UIExpandablePane advancedPane = new UIExpandablePane(com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Report_Advanced"), 280, 20, borderPane); + centerPane.add(advancedPane, BorderLayout.NORTH); + this.add(centerPane, BorderLayout.NORTH); + } + + private void initCenterPane(){ + this.setLayout(FRGUIPaneFactory.createBorderLayout()); + initUIComboBox(); + JPanel thirdPane = FRGUIPaneFactory.createBorderLayout_S_Pane(); + JPanel jPanel = createThirdPane(); + jPanel.setBorder(BorderFactory.createEmptyBorder(10, 0, 10, 0)); + thirdPane.add(jPanel, BorderLayout.CENTER); + UIExpandablePane layoutExpandablePane = new UIExpandablePane(com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Form_Layout"), 280, 20, thirdPane); + this.add(layoutExpandablePane, BorderLayout.CENTER); + + } + + public JPanel createThirdPane() { + initLayoutComboBox(); + JPanel jPanel = FRGUIPaneFactory.createBorderLayout_S_Pane(); + double p = TableLayout.PREFERRED; + double f = TableLayout.FILL; + double[] rowSize = {p}; + double[] colSize = {p, f}; + + UILabel layoutTypeLabel = FRWidgetFactory.createLineWrapLabel(com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Form_Attr_Layout_Type")); + JPanel northPane = TableLayoutHelper.createGapTableLayoutPane( + new Component[][]{ + new Component[]{layoutTypeLabel, UIComponentUtils.wrapWithBorderLayoutPane(layoutCombox)}}, + rowSize, colSize, IntervalConstants.INTERVAL_W1, IntervalConstants.INTERVAL_L1); + jPanel.add(northPane, BorderLayout.NORTH); + return jPanel; + + } + + public void initLayoutComboBox() { + Item[] items = FRLayoutTypeItems.ITEMS; + DefaultComboBoxModel model = new DefaultComboBoxModel(); + for (Item item : items) { + model.addElement(item); + } + layoutCombox = new UIComboBox(model); + layoutCombox.setSelectedIndex(1); + } + + @Override + public String title4PopupWindow() { + return "absoluteBodyLayout"; + } + + public void populateSubPane(WAbsoluteLayout ob) { + layoutCombox.setSelectedIndex(1); + borderStyleEditor.setValue(ob.getBorderStyle()); + watermarkEditor.setValue(ReportUtils.getWatermarkAttrFromTemplate(getCurrentIOFile())); + } + + public WAbsoluteBodyLayout updateSubPane() { + WAbsoluteBodyLayout layout = (WAbsoluteBodyLayout) creator.toData(); + Item item = (Item) layoutCombox.getSelectedItem(); + Object value = item.getValue(); + int state = 0; + if (value instanceof Integer) { + state = (Integer) value; + } + + if (layoutType == WBodyLayoutType.ABSOLUTE) { + ((XWFitLayout) creator.getBackupParent()).toData().resetStyle(); + if (state == WBodyLayoutType.FIT.getTypeValue()) { + XWFitLayout xwFitLayout = ((XWFitLayout)creator.getBackupParent()); + xwFitLayout.switch2FitBodyLayout(creator); + copyLayoutAttr(layout, xwFitLayout.toData()); + } + } + layout.setBorderStyle((LayoutBorderStyle) borderStyleEditor.getValue()); + updateWatermark(); + return layout; + } + + private void updateWatermark() { + WatermarkAttr watermark = (WatermarkAttr) watermarkEditor.getValue(); + if (watermark != null) { + IOFile ioFile = getCurrentIOFile(); + ioFile.addAttrMark(watermark); + } + } + + private IOFile getCurrentIOFile() { + return WidgetPropertyPane.getInstance().getEditingFormDesigner().getTarget(); + } + + @Override + public DataCreatorUI dataUI() { + return null; + } + + +} diff --git a/designer-form/src/main/java/com/fr/design/fit/attrpane/FRAbsoluteLayoutExtendDefinePane.java b/designer-form/src/main/java/com/fr/design/fit/attrpane/FRAbsoluteLayoutExtendDefinePane.java new file mode 100644 index 000000000..60ffae3eb --- /dev/null +++ b/designer-form/src/main/java/com/fr/design/fit/attrpane/FRAbsoluteLayoutExtendDefinePane.java @@ -0,0 +1,27 @@ +package com.fr.design.fit.attrpane; + +import com.fr.design.designer.creator.XCreator; +import com.fr.design.layout.FRGUIPaneFactory; +import com.fr.design.widget.ui.designer.layout.AbstractFRLayoutDefinePane; +import com.fr.form.ui.container.WAbsoluteLayout; + +/** + * Created by kerry on 2020-04-22 + */ +public class FRAbsoluteLayoutExtendDefinePane extends AbstractFRLayoutDefinePane { + + public FRAbsoluteLayoutExtendDefinePane(XCreator xCreator) { + super(xCreator); + this.setLayout(FRGUIPaneFactory.createBorderLayout()); + } + + @Override + public void populateBean(WAbsoluteLayout ob) { + } + + @Override + public WAbsoluteLayout updateBean() { + WAbsoluteLayout wAbsoluteLayout = (WAbsoluteLayout) creator.toData(); + return wAbsoluteLayout; + } +} diff --git a/designer-form/src/main/java/com/fr/design/fit/attrpane/FRFitLayoutExtendDefinePane.java b/designer-form/src/main/java/com/fr/design/fit/attrpane/FRFitLayoutExtendDefinePane.java new file mode 100644 index 000000000..3dd0f1c48 --- /dev/null +++ b/designer-form/src/main/java/com/fr/design/fit/attrpane/FRFitLayoutExtendDefinePane.java @@ -0,0 +1,246 @@ +package com.fr.design.fit.attrpane; + +import com.fr.base.io.IOFile; +import com.fr.base.iofile.attr.WatermarkAttr; +import com.fr.design.data.DataCreatorUI; +import com.fr.design.designer.IntervalConstants; +import com.fr.design.designer.creator.XCreator; +import com.fr.design.designer.creator.XLayoutContainer; +import com.fr.design.designer.creator.XWAbsoluteBodyLayout; +import com.fr.design.designer.creator.XWFitLayout; +import com.fr.design.designer.creator.XWScaleLayout; +import com.fr.design.designer.properties.items.FRLayoutTypeItems; +import com.fr.design.designer.properties.items.Item; +import com.fr.design.foldablepane.UIExpandablePane; +import com.fr.design.gui.icombobox.UIComboBox; +import com.fr.design.gui.ilable.UILabel; +import com.fr.design.gui.ispinner.UISpinner; +import com.fr.design.layout.FRGUIPaneFactory; +import com.fr.design.layout.TableLayout; +import com.fr.design.layout.TableLayoutHelper; +import com.fr.design.mainframe.FormDesigner; +import com.fr.design.mainframe.FormSelectionUtils; +import com.fr.design.mainframe.WidgetPropertyPane; +import com.fr.design.mainframe.widget.accessibles.AccessibleBodyWatermarkEditor; +import com.fr.design.mainframe.widget.accessibles.AccessibleWLayoutBorderStyleEditor; +import com.fr.design.utils.gui.UIComponentUtils; +import com.fr.design.widget.FRWidgetFactory; +import com.fr.design.widget.ui.designer.component.PaddingBoundPane; +import com.fr.design.widget.ui.designer.layout.AbstractFRLayoutDefinePane; +import com.fr.form.ui.LayoutBorderStyle; +import com.fr.form.ui.Widget; +import com.fr.form.ui.container.WAbsoluteBodyLayout; +import com.fr.form.ui.container.WAbsoluteLayout; +import com.fr.form.ui.container.WBodyLayoutType; +import com.fr.form.ui.container.WFitLayout; +import com.fr.general.ComparatorUtils; +import com.fr.log.FineLoggerFactory; +import com.fr.report.core.ReportUtils; + +import javax.swing.BorderFactory; +import javax.swing.DefaultComboBoxModel; +import javax.swing.JPanel; +import java.awt.BorderLayout; +import java.awt.Component; +import java.awt.Dimension; + +/** + * Created by kerry on 2020-04-22 + */ +public class FRFitLayoutExtendDefinePane extends AbstractFRLayoutDefinePane { + private static final int ADAPT_LABEL_MAX_WIDTH = 80; + private XWFitLayout xWFitLayout; + private WFitLayout wFitLayout; + private UIComboBox layoutComboBox; + private UISpinner componentIntervel; + private PaddingBoundPane paddingBound; + private AccessibleWLayoutBorderStyleEditor stylePane; + private AccessibleBodyWatermarkEditor watermarkEditor; + + public FRFitLayoutExtendDefinePane(XCreator xCreator) { + super(xCreator); + this.xWFitLayout = (XWFitLayout) xCreator; + wFitLayout = xWFitLayout.toData(); + initComponent(); + } + + + public void initComponent() { + this.setLayout(FRGUIPaneFactory.createBorderLayout()); + JPanel advancePane = createAdvancePane(); + UIExpandablePane advanceExpandablePane = new UIExpandablePane(com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Report_Advanced"), 280, 20, advancePane); + this.add(advanceExpandablePane, BorderLayout.NORTH); + UIExpandablePane layoutExpandablePane = new UIExpandablePane(com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Form_Layout"), 280, 20, createLayoutPane()); + this.add(layoutExpandablePane, BorderLayout.CENTER); + } + + public JPanel createAdvancePane() { + JPanel jPanel = FRGUIPaneFactory.createBorderLayout_S_Pane(); + stylePane = new AccessibleWLayoutBorderStyleEditor(); + watermarkEditor = new AccessibleBodyWatermarkEditor(); + paddingBound = new PaddingBoundPane(); + JPanel jp2 = TableLayoutHelper.createGapTableLayoutPane( + new Component[][]{ + new Component[]{new UILabel(com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Form_Widget_Style")), stylePane}, + new Component[]{new UILabel(com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Form_WaterMark")), watermarkEditor} + }, TableLayoutHelper.FILL_LASTCOLUMN, IntervalConstants.INTERVAL_W3, IntervalConstants.INTERVAL_L1); + jp2.setBorder(BorderFactory.createEmptyBorder(10, 0, 0, 0)); + jPanel.add(paddingBound, BorderLayout.CENTER); + jPanel.add(jp2, BorderLayout.NORTH); + return jPanel; + } + + public JPanel createLayoutPane() { + JPanel jPanel = FRGUIPaneFactory.createBorderLayout_S_Pane(); + jPanel.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0)); + layoutComboBox = initUIComboBox(FRLayoutTypeItems.ITEMS); + componentIntervel = new UISpinner(0, Integer.MAX_VALUE, 1, 0); + JPanel componentIntervelPane = UIComponentUtils.wrapWithBorderLayoutPane(componentIntervel); + + UILabel intervalLabel = FRWidgetFactory.createLineWrapLabel(com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Form_Component_Interval")); + + double f = TableLayout.FILL; + double p = TableLayout.PREFERRED; + double[] rowSize = {p, p}; + double[] columnSize = {p, f}; + int[][] rowCount = {{1, 1}, {1, 1}}; + + + Component[][] components = new Component[][]{ + new Component[]{new UILabel(com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Form_Attr_Layout_Type")), layoutComboBox}, + new Component[]{intervalLabel, componentIntervelPane} + }; + JPanel centerPane = TableLayoutHelper.createGapTableLayoutPane(components, rowSize, columnSize, rowCount, IntervalConstants.INTERVAL_W1, IntervalConstants.INTERVAL_L1); + centerPane.setBorder(BorderFactory.createEmptyBorder(IntervalConstants.INTERVAL_L1, 0, 0, 0)); + jPanel.add(centerPane, BorderLayout.CENTER); + return jPanel; + } + + + public UIComboBox initUIComboBox(Item[] items) { + DefaultComboBoxModel model = new DefaultComboBoxModel(); + for (Item item : items) { + model.addElement(item); + } + return new UIComboBox(model); + } + + + @Override + public String title4PopupWindow() { + return "fitLayout"; + } + + @Override + public void populateBean(WFitLayout ob) { + FormDesigner formDesigner = WidgetPropertyPane.getInstance().getEditingFormDesigner(); + XLayoutContainer rootLayout = selectedBodyLayout(formDesigner); + if (rootLayout != formDesigner.getRootComponent() + && formDesigner.getSelectionModel().getSelection().getSelectedCreator() == formDesigner.getRootComponent()) { + formDesigner.getSelectionModel().setSelectedCreators( + FormSelectionUtils.rebuildSelection(xWFitLayout, new Widget[]{selectedBodyLayout(formDesigner).toData()})); + + } + paddingBound.populate(ob); + layoutComboBox.setSelectedIndex(ob.getBodyLayoutType().getTypeValue()); + componentIntervel.setValue(ob.getCompInterval()); + stylePane.setValue(ob.getBorderStyle()); + watermarkEditor.setValue(ReportUtils.getWatermarkAttrFromTemplate(getCurrentIOFile())); + } + + private XLayoutContainer selectedBodyLayout(FormDesigner formDesigner) { + XLayoutContainer rootLayout = formDesigner.getRootComponent(); + if (rootLayout.getComponentCount() == 1 && rootLayout.getXCreator(0).acceptType(XWAbsoluteBodyLayout.class)) { + rootLayout = (XWAbsoluteBodyLayout) rootLayout.getXCreator(0); + } + return rootLayout; + } + + + @Override + public WFitLayout updateBean() { + WFitLayout layout = (WFitLayout) creator.toData(); + if (ComparatorUtils.equals(getGlobalName(), com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Form_Layout_Padding_Duplicate"))) { + paddingBound.update(layout); + } + LayoutBorderStyle borderStyle = (LayoutBorderStyle) stylePane.getValue(); + if (borderStyle != null) { + layout.setBorderStyle(borderStyle); + } + updateWatermark(); + Item item = (Item) layoutComboBox.getSelectedItem(); + Object value = item.getValue(); + int state = 0; + if (value instanceof Integer) { + state = (Integer) value; + } + //todo 验证下 + layout.setLayoutType(WBodyLayoutType.parse(state)); + try { + if (state == WBodyLayoutType.ABSOLUTE.getTypeValue()) { + WAbsoluteBodyLayout wAbsoluteBodyLayout = new WAbsoluteBodyLayout("body"); + wAbsoluteBodyLayout.setCompState(WAbsoluteLayout.STATE_FIXED); + Component[] components = xWFitLayout.getComponents(); + xWFitLayout.removeAll(); + layout.resetStyle(); + XWAbsoluteBodyLayout xwAbsoluteBodyLayout = xWFitLayout.getBackupParent() == null ? new XWAbsoluteBodyLayout(wAbsoluteBodyLayout, new Dimension(0, 0)) : (XWAbsoluteBodyLayout) xWFitLayout.getBackupParent(); + xWFitLayout.getLayoutAdapter().addBean(xwAbsoluteBodyLayout, 0, 0); + for (Component component : components) { + XCreator xCreator = (XCreator) component; + //部分控件被ScaleLayout包裹着,绝对布局里面要放出来 + if (xCreator.acceptType(XWScaleLayout.class)) { + if (xCreator.getComponentCount() > 0 && ((XCreator) xCreator.getComponent(0)).shouldScaleCreator()) { + component = xCreator.getComponent(0); + component.setBounds(xCreator.getBounds()); + } + } + xwAbsoluteBodyLayout.add(component); + } + copyLayoutAttr(wFitLayout, wAbsoluteBodyLayout); + xWFitLayout.setBackupParent(xwAbsoluteBodyLayout); + FormDesigner formDesigner = WidgetPropertyPane.getInstance().getEditingFormDesigner(); + formDesigner.getSelectionModel().setSelectedCreators( + FormSelectionUtils.rebuildSelection(xWFitLayout, new Widget[]{wAbsoluteBodyLayout})); + } + } catch (Exception e) { + FineLoggerFactory.getLogger().error(e.getMessage(), e); + + } + + int intervelValue = (int) componentIntervel.getValue(); + if (xWFitLayout.canAddInterval(intervelValue)) { +// 设置完间隔后,要同步处理界面组件,容器刷新后显示出对应效果 + setLayoutGap(intervelValue); + } + + return layout; + } + + private void updateWatermark() { + WatermarkAttr watermark = (WatermarkAttr) watermarkEditor.getValue(); + if (watermark != null) { + IOFile ioFile = getCurrentIOFile(); + ioFile.addAttrMark(watermark); + } + } + + private IOFile getCurrentIOFile() { + return WidgetPropertyPane.getInstance().getEditingFormDesigner().getTarget(); + } + + private void setLayoutGap(int value) { + int interval = wFitLayout.getCompInterval(); + if (value != interval) { + xWFitLayout.moveContainerMargin(); + xWFitLayout.moveCompInterval(xWFitLayout.getAcualInterval()); + wFitLayout.setCompInterval(value); + xWFitLayout.addCompInterval(xWFitLayout.getAcualInterval()); + } + } + + @Override + public DataCreatorUI dataUI() { + return null; + } + +} diff --git a/designer-form/src/main/java/com/fr/design/fit/attrpane/PcFitExpandablePane.java b/designer-form/src/main/java/com/fr/design/fit/attrpane/PcFitExpandablePane.java new file mode 100644 index 000000000..0bd1e2bd2 --- /dev/null +++ b/designer-form/src/main/java/com/fr/design/fit/attrpane/PcFitExpandablePane.java @@ -0,0 +1,107 @@ +package com.fr.design.fit.attrpane; + +import com.fr.design.designer.IntervalConstants; +import com.fr.design.fit.FitStateCompatible; +import com.fr.design.gui.icombobox.UIComboBox; +import com.fr.design.gui.ilable.UILabel; +import com.fr.design.layout.FRGUIPaneFactory; +import com.fr.design.layout.TableLayoutHelper; +import com.fr.design.widget.DataModify; +import com.fr.report.fit.FitAttrState; +import com.fr.report.fit.ReportFitAttr; + +import javax.swing.BorderFactory; +import javax.swing.JPanel; +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.Component; +import java.awt.event.ItemEvent; +import java.awt.event.ItemListener; + + +/** + * Created by kerry on 2020-04-22 + */ +public class PcFitExpandablePane extends JPanel { + UIComboBox comboBox = new UIComboBox( + new String[]{com.fr.design.i18n.Toolkit.i18nText("Fine-Designer_Fit_Ec_Double_Fit"), + com.fr.design.i18n.Toolkit.i18nText("Fine-Designer_Fit_Ec_Hor_Fit"), + com.fr.design.i18n.Toolkit.i18nText("Fine-Designer_Fit_Ec_No_Fit")}); + UILabel tipLabel = new UILabel(com.fr.design.i18n.Toolkit.i18nText("Fine-Designer_Fit_Ec_No_Fit_Tip")); + + public PcFitExpandablePane(DataModify attrPane) { + init(attrPane); + } + + public void init(final DataModify attrPane) { + this.setLayout(FRGUIPaneFactory.createBorderLayout()); + JPanel pcFitPane = FRGUIPaneFactory.createBorderLayout_S_Pane(); + final JPanel borderPane = FRGUIPaneFactory.createBorderLayout_S_Pane(); + borderPane.setBorder(BorderFactory.createEmptyBorder(0, 0, 5, 0)); + tipLabel.setForeground(Color.gray); + comboBox.addItemListener(new ItemListener() { + @Override + public void itemStateChanged(ItemEvent e) { + tipLabel.setText(ScaleTipType.getScaleTip(comboBox.getSelectedIndex()).getTip()); + } + }); + Component[][] components = new Component[][]{ + new Component[]{new UILabel(com.fr.design.i18n.Toolkit.i18nText("Fine-Designer_Fit_Ec_Content_Display_Type")), comboBox}, + new Component[]{tipLabel, null}, + }; + JPanel panel = TableLayoutHelper.createGapTableLayoutPane(components, TableLayoutHelper.FILL_LASTCOLUMN, IntervalConstants.INTERVAL_W0, IntervalConstants.INTERVAL_W0); + panel.setBorder(BorderFactory.createEmptyBorder(5, 0, 10, 0)); + + borderPane.add(panel, BorderLayout.CENTER); + borderPane.add(tipLabel, BorderLayout.SOUTH); + pcFitPane.add(borderPane, BorderLayout.CENTER); + this.add(pcFitPane, BorderLayout.CENTER); + } + + public ReportFitAttr update() { + ReportFitAttr fitAttr = getReportFitAttr(); + fitAttr.setFitStateInPC(FitStateCompatible.getOldTypeFromNew(comboBox.getSelectedIndex())); + return fitAttr; + } + + public void populate(ReportFitAttr fitAttr) { + if (fitAttr == null) { + fitAttr = getReportFitAttr(); + } + int selectIndex = FitStateCompatible.getNewTypeFromOld(fitAttr.fitStateInPC()); + comboBox.setSelectedIndex(selectIndex); + tipLabel.setText(ScaleTipType.getScaleTip(selectIndex).getTip()); + } + + private ReportFitAttr getReportFitAttr() { + ReportFitAttr fitAttr = new ReportFitAttr(); + fitAttr.setFitStateInPC(FitAttrState.NOT_FIT.getState()); + return fitAttr; + } + + private enum ScaleTipType { + EC_DOUBLE_FIT(0, com.fr.design.i18n.Toolkit.i18nText("Fine-Designer_Fit_Ec_Double_Fit_Tip")), + EC_HOR_FIT(1, com.fr.design.i18n.Toolkit.i18nText("Fine-Designer_Fit_Ec_Hor_Fit_Tip")), + EC_NO_FIT(2, com.fr.design.i18n.Toolkit.i18nText("Fine-Designer_Fit_Ec_No_Fit_Tip")); + private int index; + private String tip; + + ScaleTipType(int index, String tip) { + this.index = index; + this.tip = tip; + } + + public String getTip() { + return tip; + } + + public static ScaleTipType getScaleTip(int selectIndex) { + for (ScaleTipType tipType : values()) { + if (selectIndex == tipType.index) { + return tipType; + } + } + return EC_NO_FIT; + } + } +} diff --git a/designer-form/src/main/java/com/fr/design/fit/attrpane/RootDesignExtendDefinePane.java b/designer-form/src/main/java/com/fr/design/fit/attrpane/RootDesignExtendDefinePane.java new file mode 100644 index 000000000..8a8aa404a --- /dev/null +++ b/designer-form/src/main/java/com/fr/design/fit/attrpane/RootDesignExtendDefinePane.java @@ -0,0 +1,303 @@ +package com.fr.design.fit.attrpane; + +import com.fr.base.BaseUtils; +import com.fr.design.ExtraDesignClassManager; +import com.fr.design.data.DataCreatorUI; +import com.fr.design.designer.IntervalConstants; +import com.fr.design.designer.creator.CRPropertyDescriptor; +import com.fr.design.designer.creator.PropertyGroupPane; +import com.fr.design.designer.creator.XCreator; +import com.fr.design.designer.creator.XWParameterLayout; +import com.fr.design.designer.properties.PropertyTab; +import com.fr.design.file.HistoryTemplateListPane; +import com.fr.design.fit.DesignerUIModeConfig; +import com.fr.design.fit.common.TemplateTool; +import com.fr.design.foldablepane.UIExpandablePane; +import com.fr.design.fun.ParameterExpandablePaneUIProvider; +import com.fr.design.gui.ibutton.UIButtonGroup; +import com.fr.design.gui.icheckbox.UICheckBox; +import com.fr.design.gui.ilable.UILabel; +import com.fr.design.gui.ispinner.UISpinner; +import com.fr.design.gui.itextfield.UITextField; +import com.fr.design.layout.FRGUIPaneFactory; +import com.fr.design.layout.TableLayout; +import com.fr.design.layout.TableLayoutHelper; +import com.fr.design.mainframe.FormDesigner; +import com.fr.design.mainframe.FormSelection; +import com.fr.design.mainframe.JTemplate; +import com.fr.design.mainframe.widget.accessibles.AccessibleBackgroundEditor; +import com.fr.design.utils.gui.LayoutUtils; +import com.fr.design.utils.gui.UIComponentUtils; +import com.fr.design.widget.ui.designer.AbstractDataModify; +import com.fr.design.widget.ui.designer.component.UIBoundSpinner; +import com.fr.form.ui.container.WParameterLayout; +import com.fr.general.Background; +import com.fr.report.stable.FormConstants; + +import javax.swing.BorderFactory; +import javax.swing.Icon; +import javax.swing.JPanel; +import java.awt.BorderLayout; +import java.awt.Component; +import java.awt.Rectangle; +import java.util.Set; + +public class RootDesignExtendDefinePane extends AbstractDataModify { + private XWParameterLayout root; + private UISpinner designerWidth; + private UISpinner paraHeight; + private UICheckBox displayReport; + private UICheckBox useParamsTemplate; + private AccessibleBackgroundEditor background; + private UIButtonGroup hAlignmentPane; + private UITextField labelNameTextField; + //是否是新设计模式下决策报表 + private boolean newForm; + private PropertyGroupPane extraPropertyGroupPane; + + public RootDesignExtendDefinePane(XCreator xCreator) { + super(xCreator); + newForm = TemplateTool.getCurrentEditingNewJForm() != null && DesignerUIModeConfig.getInstance().newUIMode(); + this.root = (XWParameterLayout) xCreator; + initComponent(); + } + + + public void initComponent() { + this.setLayout(FRGUIPaneFactory.createBorderLayout()); + if (newForm) { + paraHeight = new UIBoundSpinner(0, Integer.MAX_VALUE, 1, 0); + } else { + designerWidth = new UIBoundSpinner(1, Integer.MAX_VALUE, 1); + } + JPanel advancePane = createAdvancePane(); + UIExpandablePane advanceExpandablePane = new UIExpandablePane(com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Report_Advanced"), 280, 20, advancePane); + this.add(advanceExpandablePane, BorderLayout.NORTH); + JPanel layoutPane = createBoundsPane(); + UIExpandablePane layoutExpandablePane = new UIExpandablePane(com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Form_Size"), 280, 20, layoutPane); + this.add(layoutExpandablePane, BorderLayout.CENTER); + this.addExtraUIExpandablePaneFromPlugin(); + } + + private void addExtraUIExpandablePaneFromPlugin() { + Set pluginCreators = ExtraDesignClassManager.getInstance().getArray(ParameterExpandablePaneUIProvider.XML_TAG); + JPanel panel = FRGUIPaneFactory.createYBoxEmptyBorderPane(); + for (ParameterExpandablePaneUIProvider provider : pluginCreators) { + UIExpandablePane uiExpandablePane = provider.createUIExpandablePane(); + PropertyTab propertyTab = provider.addToWhichPropertyTab(); + if (uiExpandablePane != null && propertyTab == PropertyTab.ATTR) { + panel.add(uiExpandablePane); + } + } + this.add(panel, BorderLayout.SOUTH); + } + + public JPanel createBoundsPane() { + double f = TableLayout.FILL; + double p = TableLayout.PREFERRED; + double[] rowSize = {p}; + double[] columnSize = {p, f}; + int[][] rowCount = {{1, 1}}; + Component[] component = newForm ? new Component[]{new UILabel(com.fr.design.i18n.Toolkit.i18nText("Fine-Designer_Fit_Design_Height")), paraHeight} : + new Component[]{new UILabel(com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Form_Desin_Width")), designerWidth}; + Component[][] components = new Component[][]{component}; + JPanel panel = TableLayoutHelper.createGapTableLayoutPane(components, rowSize, columnSize, rowCount, IntervalConstants.INTERVAL_W1, IntervalConstants.INTERVAL_L1); + JPanel jPanel = FRGUIPaneFactory.createBorderLayout_S_Pane(); + panel.setBorder(BorderFactory.createEmptyBorder(10, 0, 10, 0)); + jPanel.add(panel); + return jPanel; + } + + public JPanel createAdvancePane() { + if (newForm) { + return getNewFormAdvancePane(); + } else { + return getTemplateAdvancePane(); + } + } + + /** + * @Description: 获取的非新决策报表AdvancePane + * @param + * @return: + * @Author: Henry.Wang + * @date: 2020/11/05 15:36 + */ + private JPanel getTemplateAdvancePane() { + JPanel jPanel = FRGUIPaneFactory.createBorderLayout_S_Pane(); + labelNameTextField = new UITextField(); + displayReport = new UICheckBox(com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Form_Display_Nothing_Before_Query")); + UIComponentUtils.setLineWrap(displayReport); + useParamsTemplate = new UICheckBox(com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Form_Use_Params_Template")); + background = new AccessibleBackgroundEditor(); + Icon[] hAlignmentIconArray = {BaseUtils.readIcon("/com/fr/design/images/m_format/cellstyle/h_left_normal.png"), + BaseUtils.readIcon("/com/fr/design/images/m_format/cellstyle/h_center_normal.png"), + BaseUtils.readIcon("/com/fr/design/images/m_format/cellstyle/h_right_normal.png"),}; + Integer[] hAlignment = new Integer[]{FormConstants.LEFTPOSITION, FormConstants.CENTERPOSITION, FormConstants.RIGHTPOSITION}; + hAlignmentPane = new UIButtonGroup(hAlignmentIconArray, hAlignment); + hAlignmentPane.setAllToolTips(new String[]{com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Form_StyleAlignment_Left") + , com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Form_StyleAlignment_Center"), com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Form_StyleAlignment_Right")}); + double f = TableLayout.FILL; + double p = TableLayout.PREFERRED; + double[] rowSize = {p, p, p, p, p}; + double[] columnSize = {p, f}; + int[][] rowCount = {{1, 1}, {1, 1}, {1, 1}, {1, 1}, {1, 1}}; + Component[][] components = new Component[][]{ + new Component[]{new UILabel(com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Form_Label_Name")), labelNameTextField}, + new Component[]{new UILabel(com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Report_Base_Background")), background}, + new Component[]{displayReport, null}, + new Component[]{useParamsTemplate, null}, + new Component[]{new UILabel(com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Form_Widget_Display_Position")), hAlignmentPane} + }; + JPanel panel = TableLayoutHelper.createGapTableLayoutPane(components, rowSize, columnSize, rowCount, IntervalConstants.INTERVAL_W0, IntervalConstants.INTERVAL_L1); + panel.setBorder(BorderFactory.createEmptyBorder(IntervalConstants.INTERVAL_L1, 0, IntervalConstants.INTERVAL_L1, 0)); + CRPropertyDescriptor[] extraTableEditor = new CRPropertyDescriptor[0]; + extraTableEditor = root.getExtraTableEditor(); + extraPropertyGroupPane = new PropertyGroupPane(extraTableEditor, root); + + jPanel.add(panel, BorderLayout.NORTH); + jPanel.add(extraPropertyGroupPane, BorderLayout.CENTER); + return jPanel; + } + + /** + * @Description: 获取新决策报表的AdvancePane + * @param + * @return: + * @Author: Henry.Wang + * @date: 2020/11/05 15:36 + */ + private JPanel getNewFormAdvancePane() { + JPanel jPanel = FRGUIPaneFactory.createBorderLayout_S_Pane(); + labelNameTextField = new UITextField(); + displayReport = new UICheckBox(com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Form_Display_Nothing_Before_Query")); + UIComponentUtils.setLineWrap(displayReport); + useParamsTemplate = new UICheckBox(com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Form_Use_Params_Template")); + background = new AccessibleBackgroundEditor(); + + double f = TableLayout.FILL; + double p = TableLayout.PREFERRED; + double[] rowSize = {p, p, p, p}; + double[] columnSize = {p, f}; + int[][] rowCount = {{1, 1}, {1, 1}, {1, 1}, {1, 1}}; + Component[][] components = new Component[][]{ + new Component[]{new UILabel(com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Form_Label_Name")), labelNameTextField}, + new Component[]{new UILabel(com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Report_Base_Background")), background}, + new Component[]{displayReport, null}, + new Component[]{useParamsTemplate, null} + }; + JPanel panel = TableLayoutHelper.createGapTableLayoutPane(components, rowSize, columnSize, rowCount, IntervalConstants.INTERVAL_W0, IntervalConstants.INTERVAL_L1); + panel.setBorder(BorderFactory.createEmptyBorder(IntervalConstants.INTERVAL_L1, 0, IntervalConstants.INTERVAL_L1, 0)); + + jPanel.add(panel, BorderLayout.NORTH); + + return jPanel; + } + + @Override + public String title4PopupWindow() { + return "parameter"; + } + + @Override + public void populateBean(WParameterLayout ob) { + labelNameTextField.setText(ob.getLabelName()); + background.setValue(ob.getBackground()); + displayReport.setSelected(ob.isDelayDisplayContent()); + useParamsTemplate.setSelected(ob.isUseParamsTemplate()); + if (newForm) { + FormDesigner designer = TemplateTool.getCurrentEditingNewJForm().getFormDesign(); + paraHeight.setValue(designer.getParaHeight()); + } else { + designerWidth.setValue(ob.getDesignWidth()); + hAlignmentPane.setSelectedItem(ob.getPosition()); + + if (extraPropertyGroupPane != null) { + extraPropertyGroupPane.populate(ob); + } + } + } + + + @Override + public WParameterLayout updateBean() { + if (newForm) { + return updateNewFormBean(); + } else { + return updateTemplateBean(); + } + } + + /** + * @Description: 更新非新决策报表的bean + * @param + * @return: + * @Author: Henry.Wang + * @date: 2020/11/05 15:36 + */ + private WParameterLayout updateTemplateBean() { + WParameterLayout wParameterLayout = (WParameterLayout) creator.toData(); + wParameterLayout.setLabelName(labelNameTextField.getText()); + if (isCompsOutOfDesignerWidth((int) designerWidth.getValue())) { + designerWidth.setValue(wParameterLayout.getDesignWidth()); + } else { + wParameterLayout.setDesignWidth((int) designerWidth.getValue()); + } + wParameterLayout.setDelayDisplayContent(displayReport.isSelected()); + wParameterLayout.setUseParamsTemplate(useParamsTemplate.isSelected()); + JTemplate jTemplate = HistoryTemplateListPane.getInstance().getCurrentEditingTemplate(); + jTemplate.needAddTemplateIdAttr(useParamsTemplate.isSelected()); + wParameterLayout.setBackground((Background) background.getValue()); + wParameterLayout.setPosition((Integer) hAlignmentPane.getSelectedItem()); + return wParameterLayout; + } + + /** + * @Description: 更新新决策报表的bean + * @param + * @return: + * @Author: Henry.Wang + * @date: 2020/11/05 15:36 + */ + private WParameterLayout updateNewFormBean() { + WParameterLayout wParameterLayout = (WParameterLayout) creator.toData(); + wParameterLayout.setLabelName(labelNameTextField.getText()); + + wParameterLayout.setDelayDisplayContent(displayReport.isSelected()); + wParameterLayout.setUseParamsTemplate(useParamsTemplate.isSelected()); + JTemplate jTemplate = HistoryTemplateListPane.getInstance().getCurrentEditingTemplate(); + jTemplate.needAddTemplateIdAttr(useParamsTemplate.isSelected()); + wParameterLayout.setBackground((Background) background.getValue()); + //设置参数模板面板的高度 + int height = (int) paraHeight.getTextField().getValue(); + FormDesigner designer = TemplateTool.getCurrentEditingNewJForm().getFormDesign(); + FormSelection selection = designer.getSelectionModel().getSelection(); + XCreator creator = designer.getParaComponent(); + Rectangle rectangle = creator.getBounds(); + Rectangle newRectangle = new Rectangle(rectangle.x, rectangle.y, rectangle.width, height); + creator.setBounds(newRectangle); + if (paraHeight.getValue() != newRectangle.height) + paraHeight.setValue(newRectangle.height); + designer.setParaHeight(newRectangle.height); + designer.getArea().doLayout(); + LayoutUtils.layoutContainer(creator); + selection.fixCreator(designer); + designer.repaint(); + return wParameterLayout; + } + + private boolean isCompsOutOfDesignerWidth(int designerWidth) { + for (int i = 0; i < root.getComponentCount(); i++) { + Component comp = root.getComponent(i); + if (comp.getX() + comp.getWidth() > designerWidth) { + return true; + } + } + return false; + } + + @Override + public DataCreatorUI dataUI() { + return null; + } +} \ No newline at end of file diff --git a/designer-form/src/main/java/com/fr/design/fit/common/AdaptiveSwitchUtil.java b/designer-form/src/main/java/com/fr/design/fit/common/AdaptiveSwitchUtil.java new file mode 100644 index 000000000..5d9c9463c --- /dev/null +++ b/designer-form/src/main/java/com/fr/design/fit/common/AdaptiveSwitchUtil.java @@ -0,0 +1,225 @@ +package com.fr.design.fit.common; + +import com.fr.design.data.DesignTableDataManager; +import com.fr.design.fit.DesignerUIModeConfig; +import com.fr.design.fit.NewJForm; +import com.fr.design.fit.attrpane.ElementEditorExtendDefinePane; +import com.fr.design.fit.attrpane.FRAbsoluteBodyLayoutExtendDefinePane; +import com.fr.design.fit.attrpane.FRAbsoluteLayoutExtendDefinePane; +import com.fr.design.fit.attrpane.FRFitLayoutExtendDefinePane; +import com.fr.design.fit.attrpane.RootDesignExtendDefinePane; +import com.fr.design.mainframe.DesignerContext; +import com.fr.design.mainframe.JForm; +import com.fr.design.mainframe.JTemplate; +import com.fr.design.mainframe.JTemplateFactory; +import com.fr.design.parameter.RootDesignDefinePane; +import com.fr.design.preview.FormAdaptivePreview; +import com.fr.design.preview.FormPreview; +import com.fr.design.widget.Appearance; +import com.fr.design.widget.FormWidgetDefinePaneFactoryBase; +import com.fr.design.widget.ui.designer.layout.ElementEditorDefinePane; +import com.fr.design.widget.ui.designer.layout.FRAbsoluteBodyLayoutDefinePane; +import com.fr.design.widget.ui.designer.layout.FRAbsoluteLayoutDefinePane; +import com.fr.design.widget.ui.designer.layout.FRFitLayoutDefinePane; +import com.fr.file.FILE; +import com.fr.file.MemFILE; +import com.fr.form.ui.ElementCaseEditor; +import com.fr.form.ui.Widget; +import com.fr.form.ui.container.WAbsoluteBodyLayout; +import com.fr.form.ui.container.WAbsoluteLayout; +import com.fr.form.ui.container.WFitLayout; +import com.fr.form.ui.container.WParameterLayout; +import com.fr.form.ui.widget.CRBoundsWidget; +import com.fr.log.FineLoggerFactory; +import com.fr.report.fit.ReportFitAttr; + +import javax.swing.SwingUtilities; + +/** + * Created by kerry on 2020-06-02 + */ +public class AdaptiveSwitchUtil { + + private static int switchJFromIng = 0; + + private AdaptiveSwitchUtil() { + + } + + public static void switch2NewUI() { + switch2NewUIMode(); + reload(); + } + + public static void switch2NewUIMode() { + DesignerUIModeConfig.getInstance().setNewUIMode(); + FormWidgetDefinePaneFactoryBase.registerDefinePane(ElementCaseEditor.class, + new Appearance(ElementEditorExtendDefinePane.class, "elementCaseEditor")); + FormWidgetDefinePaneFactoryBase.registerDefinePane(WFitLayout.class, + new Appearance(FRFitLayoutExtendDefinePane.class, "wFitLayout")); + FormWidgetDefinePaneFactoryBase.registerDefinePane(WAbsoluteBodyLayout.class, + new Appearance(FRAbsoluteBodyLayoutExtendDefinePane.class, "wAbsoluteBodyLayout")); + FormWidgetDefinePaneFactoryBase.registerDefinePane(WAbsoluteLayout.class, + new Appearance(FRAbsoluteLayoutExtendDefinePane.class, "wAbsoluteLayout")); + FormWidgetDefinePaneFactoryBase.registerDefinePane(WParameterLayout.class, + new Appearance(RootDesignExtendDefinePane.class, "wParameterLayout")); + } + + public static void switch2OldUI() { + switch2OldUIMode(); + reload(); + } + + public static void switch2OldUIMode() { + DesignerUIModeConfig.getInstance().setOldUIMode(); + FormWidgetDefinePaneFactoryBase.registerDefinePane(WAbsoluteLayout.class, + new Appearance(FRAbsoluteLayoutDefinePane.class, "wAbsoluteLayout")); + FormWidgetDefinePaneFactoryBase.registerDefinePane(ElementCaseEditor.class, + new Appearance(ElementEditorDefinePane.class, "elementCaseEditor")); + FormWidgetDefinePaneFactoryBase.registerDefinePane(WAbsoluteBodyLayout.class, + new Appearance(FRAbsoluteBodyLayoutDefinePane.class, "wAbsoluteBodyLayout")); + FormWidgetDefinePaneFactoryBase.registerDefinePane(WFitLayout.class, + new Appearance(FRFitLayoutDefinePane.class, "wFitLayout")); + FormWidgetDefinePaneFactoryBase.registerDefinePane(WParameterLayout.class, + new Appearance(RootDesignDefinePane.class, "wParameterLayout")); + } + + public static void reload() { + SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + synchronized (AdaptiveSwitchUtil.class) { + try { + if (DesignerContext.getDesignerFrame() == null) { + return; + } + JTemplate old = TemplateTool.getCurrentEditingTemplate(); + if (old == null || !(old instanceof JForm)) { + return; + } + JTemplate template = createNewJTemplate(old); + if (template != null) { + DesignTableDataManager.closeTemplate(old); + TemplateTool.resetTabPaneEditingTemplate(template); + TemplateTool.activeAndResizeTemplate(template); + setPreviewType(); + } + } catch (Exception e) { + FineLoggerFactory.getLogger().error(e.getMessage()); + } finally { + switchJFromIng = 0; + } + } + } + }); + } + + /** + * @Description: 设置预览方式 + * @return: 新创建的模板 + * @Author: Henry.Wang + * @date: 2020/10/13 14:08 + */ + private static void setPreviewType() { + JTemplate jTemplate = TemplateTool.getCurrentEditingTemplate(); + if (jTemplate != null) { + if (DesignerUIModeConfig.getInstance().newUIMode()) { + jTemplate.setPreviewType(new FormAdaptivePreview()); + } else { + jTemplate.setPreviewType(new FormPreview()); + } + } + } + + /** + * @Description: 创建模板 + * @param old 以前的模板 + * @return: 新创建的模板 + * @Author: Henry.Wang + * @date: 2020/9/6 14:08 + */ + public static JTemplate createNewJTemplate(JTemplate old) { + JTemplate template; + template = createNewJTemplateInternal(old); + if (template instanceof NewJForm) { + NewJForm jForm = ((NewJForm) template); + //如果是从旧的设计模板转化为新的设计模式,并且不是全局配置的模板。则更新新模板的Pc端自适应属性 + if (DesignerUIModeConfig.getInstance().newUIMode() && jForm.getTarget().getReportFitAttr() != null && isSwitchJFromIng()) { + jForm.getTarget().setReportFitAttr(shiftReportFitAttr(old, jForm.getTarget().getReportFitAttr().isFitFont())); + } + TemplateTool.saveForm(jForm); + } + return template; + } + + /** + * @Description: 创建模板核心方法 + * @param old 以前的方法 + * @return: 新创建的模板 + * @Author: Henry.Wang + * @date: 2020/9/6 14:09 + */ + private static JTemplate createNewJTemplateInternal(JTemplate old) { + FILE file = old.getEditingFILE(); + if ((file instanceof MemFILE) || !old.isSaved()) { + if (old instanceof NewJForm) { + TemplateTool.resetAbsoluteBodySize((NewJForm) old); + } + TemplateTool.saveForm(old); + } + return JTemplateFactory.createJTemplate(old.getEditingFILE()); + } + + /** + * @Description: 老模板切换到新模板的属性配置转换( + * 1、绝对布局-适应区域--》双向自适应 + * 2、自适应布局-双向自适应--》双向自适应 + * 3、自适应布局-横向自适应--》横向自适应 + * 4、绝对布局-固定大小--》不自适应 + * ) + * @param old + * @param fitFont 字体是否自适应 + * @return: + * @Author: Henry.Wang + * @date: 2020/9/6 14:01 + */ + private static ReportFitAttr shiftReportFitAttr(JTemplate old, boolean fitFont) { + if (old instanceof JForm && DesignerUIModeConfig.getInstance().newUIMode()) { + JForm jForm = (JForm) old; + try { + int layoutType = LayoutTool.getFormLayoutType(jForm); + int compState = -1; + //自适应布局 + if (layoutType == 0) { + compState = ((WFitLayout) jForm.getFormDesign().getRootComponent().toData()).getCompState(); + if (compState == WFitLayout.STATE_FULL) { + return new ReportFitAttr(2, fitFont); + } else if (compState == WFitLayout.STATE_ORIGIN) { + return new ReportFitAttr(1, fitFont); + } + } else if (layoutType == 1) {//绝对布局 + Widget widget = ((CRBoundsWidget) jForm.getFormDesign().getRootComponent().toData().getWidget(0)).getWidget(); + if (widget instanceof WAbsoluteLayout) { + compState = ((WAbsoluteLayout) widget).getCompState(); + } + if (compState == WAbsoluteLayout.STATE_FIT) { + return new ReportFitAttr(2, fitFont); + } else if (compState == WAbsoluteLayout.STATE_FIXED) { + return new ReportFitAttr(3, fitFont); + } + } + } catch (Exception e) { + FineLoggerFactory.getLogger().error(e.getMessage(), e); + } + } + return null; + } + + public static void setSwitchJFromIng(int switchJFromIng) { + AdaptiveSwitchUtil.switchJFromIng = switchJFromIng; + } + + public static boolean isSwitchJFromIng() { + return switchJFromIng == 1; + } +} diff --git a/designer-form/src/main/java/com/fr/design/fit/common/BaseUtils.java b/designer-form/src/main/java/com/fr/design/fit/common/BaseUtils.java new file mode 100644 index 000000000..829d320e8 --- /dev/null +++ b/designer-form/src/main/java/com/fr/design/fit/common/BaseUtils.java @@ -0,0 +1,1266 @@ +package com.fr.design.fit.common; + +import com.fr.base.AutoChangeLineAndDrawManager; +import com.fr.base.FRContext; +import com.fr.base.GraphHelper; +import com.fr.base.Style; +import com.fr.base.SynchronizedLiveDataModelUtils; +import com.fr.base.TableData; +import com.fr.base.regist.LicenseConfigManager; +import com.fr.config.ServerPreferenceConfig; +import com.fr.data.api.TableDataAssist; +import com.fr.design.fit.PX; +import com.fr.general.ComparatorUtils; +import com.fr.general.FArray; +import com.fr.general.FRFont; +import com.fr.general.GeneralContext; +import com.fr.general.GeneralUtils; +import com.fr.general.IOUtils; +import com.fr.general.data.DataModel; +import com.fr.general.data.TableDataException; +import com.fr.general.xml.GeneralXMLTools; +import com.fr.json.JSONArray; +import com.fr.json.JSONException; +import com.fr.json.JSONObject; +import com.fr.json.JSONUtils; +import com.fr.json.revise.EmbedJson; +import com.fr.locale.InterProviderFactory; +import com.fr.log.FineLoggerFactory; +import com.fr.plugin.injectable.PluginModule; +import com.fr.report.fun.VerticalTextProcessor; +import com.fr.report.fun.impl.DefaultVerticalTextProcessor; +import com.fr.script.Calculator; +import com.fr.stable.ColumnRow; +import com.fr.stable.Constants; +import com.fr.stable.GraphDrawHelper; +import com.fr.stable.ListSet; +import com.fr.stable.StableUtils; +import com.fr.stable.StringUtils; +import com.fr.stable.bridge.ObjectHolder; +import com.fr.stable.fun.AutoChangeLineAndDrawProcess; +import com.fr.stable.fun.FontProcessor; +import com.fr.stable.plugin.ExtraClassManagerProvider; +import com.fr.stable.project.ProjectConstants; +import com.fr.stable.script.CalculatorProvider; +import com.fr.stable.unit.FU; +import com.fr.stable.unit.UNIT; +import com.fr.workspace.WorkContext; + +import javax.swing.Icon; +import javax.swing.ImageIcon; +import javax.swing.JFrame; +import java.awt.Color; +import java.awt.Font; +import java.awt.FontMetrics; +import java.awt.Graphics2D; +import java.awt.Image; +import java.awt.Paint; +import java.awt.geom.AffineTransform; +import java.awt.geom.Dimension2D; +import java.awt.image.BufferedImage; +import java.awt.image.CropImageFilter; +import java.awt.image.FilteredImageSource; +import java.io.IOException; +import java.io.InputStream; +import java.text.NumberFormat; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Iterator; +import java.util.List; +import java.util.Locale; +import java.util.Map; + + +/** + * 基本的工具类 + */ +public class BaseUtils { + + private BaseUtils() { + } + + /** + * 从缓存中读取图片(如果缓存中存在的话则直接从缓存中读取,如果不存在则从磁盘读取并将读取的结果缓存起来) + * + * @param resource 图片的路径 + * @return 图片 + * @see IOUtils#readImageWithCache(String) + * @deprecated + */ + public static BufferedImage readImageWithCache(String resource) { + return IOUtils.readImageWithCache(resource); + } + + /** + * 从缓存中读取图片,如果缓存中不存在则返回null + * + * @param resource 图片路径 + * @return 图片,缓存中没有则返回null + * @see IOUtils#readCacheImage(String) + * @deprecated + */ + public static BufferedImage readCacheImage(String resource) { + return IOUtils.readCacheImage(resource); + } + + /** + * 直接从磁盘中读取图片,这种方法效率稍低但每次图片更改了也能即时反应出来 + * + * @param resource 图片的路径 + * @return 图片 + * @see IOUtils#readImage(String) + * @deprecated + */ + public static BufferedImage readImage(String resource) { + //daniel bug 5400 图片读取 默认关闭缓存 + return IOUtils.readImage(resource); + } + + /** + * 读取图标,该方法启用了缓存 + * + * @param resource 图标文件的存放路径 + * @return 图标 + * @see IOUtils#readIcon(String) + * @deprecated + */ + public static Icon readIcon(String resource) { + return IOUtils.readIcon(resource); + } + + /** + * 从输入流中读取图片 + * + * @param input 输入流 + * @return 图片 + * @throws IOException + * @see IOUtils#readImage(InputStream) + * @deprecated + */ + public static BufferedImage readImage(InputStream input) throws IOException { + return IOUtils.readImage(input); + } + + /** + * 将行列对象转换成其字符串的表示形式 + * 转换后的字符串将像A2,B4,C45这样的 + *

+ * 将A1单元格转化成字符串"A1": + *

+ * BaseUtils.convertColumnRowToCellString(ColumnRow.valueOf(0,0)); + * + * @param columnRow 要转换的行列对象 + * @return 表示行列位置的字符串 + */ + public static String convertColumnRowToCellString(ColumnRow columnRow) { + if (columnRow == null) { + return ""; + } + + return columnRow.toString(); + } + + /** + * 将表示行列位置的字符串转换成行列对象 + * 如果转换失败,将会返回{@link ColumnRow} + * + * @param columnRowString 表示行列位置的字符串对象 + * @return 行列对象 + * @see ColumnRow#valueOf(String) + * @deprecated + */ + public static ColumnRow convertCellStringToColumnRow(String columnRowString) { + return ColumnRow.valueOf(columnRowString); + } + + /** + * 读取jar的版本号 + * + * @return 版本号 + * @see GeneralUtils#readBuildNO() + * @deprecated + */ + public static String readBuildNO() { + return GeneralUtils.readBuildNO(); + } + + /** + * 把指定位置的资源以默认的GBK编码的形式读取成字符串 + * + * @param path 资源存放的路径 + * @return 表示资源内容的字符串 + * @see IOUtils#readResourceAsString(String) + * @deprecated + */ + public static String readResourceAsString(String path) { + return IOUtils.readResourceAsString(path); + } + + /** + * 将指定位置的资源以指定的编码形式读取成字符串 + * + * @param path 资源存放的路径 + * @param encode 读取资源所用的编码 + * @return 表示资源内容的字符串 + * @see IOUtils#readResourceAsString(String, String) + * @deprecated + */ + public static String readResourceAsString(String path, String encode) { + return IOUtils.readResourceAsString(path, encode); + } + + @Deprecated + public static String[] getDependence(Object value, CalculatorProvider c) { + + try { + return (String[]) StableUtils.invokeMethod("com.fr.form.FormUtils", "getDependence", new Class[]{Object.class, CalculatorProvider.class}, new Object[]{value, c}); + } catch (Exception e) { + FineLoggerFactory.getLogger().error(e.getMessage(), e); + } + return null; + } + + /** + * 读取资源 + * + * @param path 资源存放的路径,可以是一个URL + * @return 返回资源的输入流 + * @see IOUtils#readResource(String) + * @deprecated + */ + public static InputStream readResource(String path) { + return IOUtils.readResource(path); + } + + /** + * 截取图片的指定区域作为一个图标 + * + * @param image 原始的图片 + * @param startx 截取的横向起始位置 + * @param starty 截取的纵向起始位置 + * @param width 截取的图标的宽度 + * @param height 截取的图标的高度 + * @return 图标 + */ + public static ImageIcon createIcon(Image image, int startx, int starty, int width, int height) { + JFrame jc = new JFrame(); + Image cropImage = jc.createImage(new FilteredImageSource(image.getSource(), new CropImageFilter(startx, starty, width, height))); + return new ImageIcon(cropImage); + } + + /** + * 将一个长整形数组以指定的分隔符转化成一个字符串 + * + * @param array 数组 + * @param decimal 分隔符 + * @return 表示数组的字符串 + */ + public static String toLongArrayString(long[] array, char decimal) { + StringBuffer sb = new StringBuffer(); + if (array != null) { + for (int i = 0; i < array.length; i++) { + if (i != 0) { + sb.append(decimal); + } + sb.append(array[i]); + } + } + return sb.toString(); + } + + /** + * 将一个有规则的字符串按指定的分隔符分割并转化成一个长整型数组 + * + * @param str 有规则的字符串 + * @param decimal 分隔符 + * @return 长整型数组 + */ + public static long[] toLongArrayFromStr(String str, char decimal) { + List list = new ArrayList(); + if (str == null) { + return new long[0]; + } + String[] strarray = StableUtils.splitString(str, String.valueOf(decimal)); + for (int i = 0; i < strarray.length; i++) { + String text = strarray[i].trim(); + list.add(Long.valueOf(text)); + } + + long[] res = new long[list.size()]; + for (int i = 0; i < res.length; i++) { + res[i] = ((Long) list.get(i)).longValue(); + } + + return res; + } + + + /** + * 获取样式中字体的类型 + * + * @param g2d 图形上下文 + * @param style 样式 + * @param resolution 屏幕分辨率 + * @return 字体 + */ + public static Font getStyleFont(Graphics2D g2d, Style style, int resolution) { + FRFont font = style.getFRFont(); + Font rfont = initFontWithLocaleAndG2d(g2d, font, resolution); + if (!ComparatorUtils.equals(rfont, g2d.getFont())) { + g2d.setFont(rfont); + } + Color foreground = font.getForeground(); + if (!ComparatorUtils.equals(foreground, g2d.getPaint())) { + g2d.setPaint(foreground); + } + + return rfont; + } + + + private static Font initFontWithLocaleAndG2d(Graphics2D g2d, FRFont font, int resolution) { + Locale locale = GeneralContext.getLocale(); + Font rfont; + if (ComparatorUtils.equals(Locale.ENGLISH, locale)) { + rfont = FRFont.getInstance("Dialog", font.getStyle(), font.getSize()); + } + + rfont = font.applyResolutionNP(resolution); + //itext的bug 用SimSun导出无法实现粗体斜体, 其作者解释是使用basefont是不支持这些style的, 要转成itext的亚洲字体 + //这边不用instance of的原因就是不想把itext包引到applet包里面去. + if (g2d.toString().indexOf("PdfGraphics2D") != -1 && ComparatorUtils.equals(FRFont.DEFAULT_FONTNAME, font.getName())) { + //不要把这边的'宋体'改成'Simsun', 就这么设定的 + rfont = FRFont.getInstance(InterProviderFactory.getProvider().getLocText("Fine-Core_Base_Song_TypeFace"), rfont.getStyle(), rfont.getSize(), + font.getForeground(), font.getUnderline(), font.isStrikethrough(), font.isShadow(), + font.isSuperscript(), font.isSubscript()); + } + + return rfont; + } + + /** + * 获取样式中关于水平方向上的对齐类型 + * + * @param style 样式 + * @return 水平方向上的对齐类型 + */ + public static int getAlignment4Horizontal(Style style) { + return getAlignment4Horizontal(style, null); + } + + /** + * 获取DataMoodel中第columnIndex列的数据. + * + * @param model 取数的数据来源 + * @param columnIndex 取数的数据列序号 + * @return 返回数据数组Object[] + */ + public static Object[] getDistinctValues(DataModel model, int columnIndex) throws TableDataException { + ListSet list = new ListSet(); + if (columnIndex != DataModel.COLUMN_NAME_NOT_FOUND) { + for (int i = 0, len = model.getRowCount(); i < len; i++) { + list.add(model.getValueAt(i, columnIndex)); + } + } + + return list.toArray(); + } + + /** + * 获取水平方向上的对齐样式 + * + * @param style 样式 + * @param value 单元格的值,默认情况下,当不设置对齐类型时,如果单元格的值是数字则靠右对齐,字符串则靠左对齐 + * @return 水平方向上的对齐样式 + */ + public static int getAlignment4Horizontal(Style style, Object value) { + int horizontalAlignment = style.getHorizontalAlignment(); + //若是默认值:判断 bug5188 数字居右 + if (value != null && horizontalAlignment == Constants.NULL) { + if (value instanceof String) { + if (style.getFormat() instanceof NumberFormat || StableUtils.isNumber((String) value)) { + horizontalAlignment = Constants.RIGHT; + } else { + //字符串:靠左 + horizontalAlignment = Constants.LEFT; + } + } else if (value instanceof Number) { + horizontalAlignment = Constants.RIGHT; + } + } + return horizontalAlignment; + } + + /** + * 基本的画文本的方法,只考虑样式中的字体和对齐方式 + * + * @param g2d 图形上下文 + * @param width 画文本的区域的宽度 + * @param height 画文本的区域的高度 + * @param text 要画的文本 + * @param style 样式 + * @param resolution 屏幕分辨率 + */ + public static void drawStringStyleInRotation(Graphics2D g2d, int width, int height, String text, Style style, int resolution) { + if (StringUtils.isBlank(text)) { + return; + } + + Paint oldPaint = g2d.getPaint(); + Font oldFont = g2d.getFont(); + + if (style == null) { + style = Style.DEFAULT_STYLE; + } + + Font font = getStyleFont(g2d, style, resolution); + font = readExtraFont(g2d, font); + int horizontalAlignment = BaseUtils.getAlignment4Horizontal(style, text); + + if (style.getRotation() != 0 && style.getVerticalText() == Style.HORIZONTALTEXT) { + drawHorizontalText(g2d, text, font, style, width, height, horizontalAlignment); + } else { + drawRotationText(g2d, text, style, font, width, height, horizontalAlignment, resolution); + } + + g2d.setFont(oldFont); + g2d.setPaint(oldPaint); + } + + private static Font readExtraFont(Graphics2D g2d, Font font) { + ExtraClassManagerProvider pluginProvider = PluginModule.getAgent(PluginModule.ExtraCore); + if (pluginProvider != null) { + FontProcessor processor = pluginProvider.getSingle(FontProcessor.MARK_STRING); + if (processor != null) { + font = processor.readExtraFont(font); + g2d.setFont(font); + } + } + return font; + } + + private static void drawHorizontalText(Graphics2D g2d, String text, Font rfont, Style style, double width, double height, int horizontalAlignment) { + AffineTransform trans = new AffineTransform(); + trans.rotate(-Math.toRadians(style.getRotation())); + + double textX = width / 2.0; + double textY = height / 2.0; + + Dimension2D textDimension = GraphHelper.stringDimensionWithRotation(text, rfont, -style.getRotation(), g2d.getFontRenderContext()); + + if (textDimension.getWidth() < width) { + if (horizontalAlignment == Constants.LEFT) { + textX = textDimension.getWidth() / 2.0; + } else if (horizontalAlignment == Constants.RIGHT) { + textX = width - textDimension.getWidth() / 2.0 - style.getPaddingLeft() * + Math.cos(Math.toRadians(style.getRotation())); + } else { + } + } + + // adjust y, height. + if (textDimension.getHeight() < height) { + if (style.getVerticalAlignment() == Constants.TOP) { + textY = textDimension.getHeight() / 2.0; + } else if (style.getVerticalAlignment() == Constants.BOTTOM) { + textY = height - textDimension.getHeight() / 2.0 - style.getPaddingLeft() * Math.sin(Math.toRadians(style.getRotation())); + } else { + } + } + + GraphHelper.drawRotatedString(g2d, text, textX, textY, -style.getRotation()); + } + + private static void drawRotationText(Graphics2D g2d, String text, Style style, Font rfont, int width, int height, int horizontalAlignment, int resolution) { + AutoChangeLineAndDrawProcess process = AutoChangeLineAndDrawManager.getProcess(); + if (process != null) { + process.drawRotationText(g2d, text, new ObjectHolder(style), rfont, width, height, horizontalAlignment, resolution); + return; + } + + FontMetrics cellFM = GraphHelper.getFontMetrics(rfont); + List lineTextList = BaseUtils.getLineTextList(text, style, rfont, height, width, resolution); + + if (width <= 0 || lineTextList.isEmpty()) { + return; + } + + int textAscent = cellFM.getAscent(); + int textHeight = cellFM.getHeight(); + + int textY = calculateTextY(style, height, textHeight, textAscent, lineTextList, resolution); + + int maxWidth = 0; + for (int i = 0; i < lineTextList.size(); i++) { + String paint_str = (String) lineTextList.get(i); + int textWidth = cellFM.stringWidth(paint_str); + if (textWidth > maxWidth) { + maxWidth = textWidth; + } + } + for (int i = 0; i < lineTextList.size(); i++) { + String paint_str = (String) lineTextList.get(i); + //把自定义角度为0的横排和竖排区分开来 + if (style.getRotation() == 0 && style.getVerticalText() == style.HORIZONTALTEXT) { + maxWidth = cellFM.stringWidth(paint_str); + } + boolean textLonger = false;//KevinWang: 标志一下:是否串长大于列宽 + if (maxWidth > width - style.getPaddingLeft() - style.getPaddingRight()) { + textLonger = true;//added by KevinWang <处理分散对齐时使用> + } //待会串长大于列宽时需要特别处理:只从中抽取合适长度的串值显示 + + int textX = (int) calculateTextX(style, width, maxWidth, horizontalAlignment, resolution); + int textWidth = width - style.getPaddingRight(); + if (isAutomaticLine(style, horizontalAlignment, textLonger)) {//自动换行时 + GraphHelper.drawString2(g2d, paint_str, textX, textY, textWidth); + } else if (isDistributeAlign(style, horizontalAlignment, textLonger)) { + drawTextWihenDistributeAlign(g2d, paint_str, cellFM, textWidth, textX, textY); + } else { //这里不能删除 + GraphHelper.drawString(g2d, paint_str, textX, textY); + } + textY += textHeight;// TODO 只增加了Y. + textY += PX.toPixWithResolution(style.getLineSpacing(), resolution); + } + } + + public static int calculateTextY(Style style, int height, int textHeight, int textAscent, List lineTextList, int resolution) { + return calculateTextY(style, height, textHeight, textAscent, lineTextList, resolution, 1); + } + + /** + * 计算Y的高度 + * + * @param style 样式 + * @param height 总高度 + * @param textHeight 文本高度 + * @param textAscent 字体的基线到大多数字母数字字符顶部的距离 + * @param lineTextList 文本列 + * @param resolution 分辨率 + * @return Y高度 + */ + public static int calculateTextY(Style style, int height, int textHeight, int textAscent, List lineTextList, int resolution, int scale) { + // 计算Y的高度. + int textY = 0; + int textAllHeight = textHeight * lineTextList.size(); + double spacingBefore = PX.toPixWithResolution(style.getSpacingBefore() * scale, resolution); + double spacingAfter = PX.toPixWithResolution(style.getSpacingAfter() * scale, resolution); + double lineSpacing = PX.toPixWithResolution(style.getLineSpacing() * scale, resolution); + textAllHeight += spacingBefore + spacingAfter + lineSpacing * lineTextList.size(); + if (style.getVerticalAlignment() == Constants.TOP) { + } else if (style.getVerticalAlignment() == Constants.CENTER) { + if (height > textAllHeight) {// 如果所有文本的高度小于当前可以绘区域的高度,就从0开始画字符. + textY = (height - textAllHeight) / 2; + } + } else if (style.getVerticalAlignment() == Constants.BOTTOM) { + if (height > textAllHeight) { + textY = height - textAllHeight; + } + } + textY += textAscent;// 在绘画的时候,必须添加Ascent的高度. + textY += spacingBefore + lineSpacing;//james:加上"段前间距"+“行间距” + return textY; + } + + + public static int calculateTextX(Style style, Font rfont, String paint_str, int width, int textWidth, int horizontalAlignment) { + return calculateTextX(style, rfont, paint_str, width, textWidth, horizontalAlignment, 1); + } + + /** + * 计算X宽度 + * + * @param style 样式 + * @param rfont 字体 + * @param paint_str 字符串 + * @param width 宽度 + * @param textWidth 文本宽度 + * @param horizontalAlignment 垂向对齐 + * @return X宽度 + */ + public static int calculateTextX(Style style, Font rfont, String paint_str, int width, int textWidth, int horizontalAlignment, int scale) { + int textX = style.getPaddingLeft() * scale; + if (horizontalAlignment == Constants.CENTER) { + textX += (width - textWidth - textX) / 2; + } else if (horizontalAlignment == Constants.RIGHT) { + textX = width - style.getPaddingRight() * scale - textWidth; +//// // TODO alex:just for flash print && for font.Tahoma +// if (Boolean.TRUE == FRCoreContext.TMAP.get() +// && ComparatorUtils.equals(rfont.getFontName(), "Tahoma")) { +// textX -= paint_str.length() / 3; +// } + } + return textX; + } + + /** + * 计算X宽度 + * + * @param style 样式 + * @param width 宽度 + * @param textWidth 文本宽度 + * @param horizontalAlignment 垂向对齐 + * @param resolution 分辨率 + * @return X宽度 + */ + public static double calculateTextX(Style style, int width, int textWidth, int horizontalAlignment, int resolution) { + return calculateTextX(style, width, textWidth, horizontalAlignment, resolution, 1); + } + + /** + * 计算文本X位置 + * + * @param style 样式 + * @param width 宽度 + * @param textWidth 文本宽度 + * @param horizontalAlignment 水平对齐 + * @return X宽度 + */ + public static double calculateTextX(Style style, int width, int textWidth, int horizontalAlignment, int resolution, int scale) { + double textX = padding2PixExcludeRight(style.getPaddingLeft(), resolution); + if (horizontalAlignment == Constants.CENTER) { + textX += (width - textWidth - textX) / 2f; + } else if (horizontalAlignment == Constants.RIGHT) { + textX = width - style.getPaddingRight() * scale - textWidth; + } + return textX; + } + + /** + * 将缩减、段间距转为对应dpi下的pix Ps:除了右缩进 + * + * @param padding + * @param resolution + * @return + */ + public static double padding2PixExcludeRight(int padding, int resolution) { + return PX.toPixWithResolution(padding, resolution); + } + + /** + * 将右缩进转为对应dpi下的pix + * + * @param paddingRight + * @param resolution + * @return + */ + public static double paddingRight2Pix(int paddingRight, int resolution) { + if (paddingRight == Style.DEFAULT_PADDING) { + return 0; + } + return paddingRight; + } + + public static boolean isAutomaticLine(Style style, int horizontalAlignment, boolean textLonger) { + return horizontalAlignment == Constants.DISTRIBUTED + && style.getTextStyle() == Style.TEXTSTYLE_WRAPTEXT + || horizontalAlignment == Constants.DISTRIBUTED + && style.getTextStyle() == Style.TEXTSTYLE_SINGLELINE + && !textLonger; + } + + public static boolean isDistributeAlign(Style style, int horizontalAlignment, boolean textLonger) { + return horizontalAlignment == Constants.DISTRIBUTED + && style.getTextStyle() == Style.TEXTSTYLE_SINGLELINE + && textLonger; + } + + public static void drawTextWihenDistributeAlign(Graphics2D g2d, String paint_str, FontMetrics cellFM, int width, int textX, int textY) { + drawTextWihenDistributeAlign(g2d, paint_str, cellFM, width, textX, textY, 1f); + } + + /** + * 画字符串:分散对齐 + * @param scale 缩放比例,主目前要用于删除线、下划线及阴影的绘制 + */ + public static void drawTextWihenDistributeAlign(Graphics2D g2d, String paint_str, FontMetrics cellFM, int width, int textX, int textY, float scale) { + String lineText = getSingleLineText(paint_str, cellFM, width); + if (StringUtils.isNotEmpty(lineText)) { + GraphDrawHelper.drawString4DistributeAlign(g2d, lineText, textX, textY, width, scale); + } + } + + /** + * 获取用于单行显示的text + * + * @param lineText 分行显示时第i行的内容,但是也可能单行 + * @param cellFM 字体 + * @param width 宽度 + * @return 拆分后的字符串 + */ + private static String getSingleLineText(String lineText, FontMetrics cellFM, int width) { + // 单行显示时的分散对齐实现e + // 串长大于列宽时需要特别处理:只从中抽取合适长度的串值显示 + StringBuffer strBuff = new StringBuffer(); + for (int charIndex = 0; charIndex < lineText.length(); charIndex++) { + strBuff.append(lineText.charAt(charIndex)); + int buffWidth = cellFM.stringWidth(new String(strBuff)); + if (buffWidth > width) { //长度足够了,开始显示 + return strBuff.substring(0, 0 == charIndex ? 0 : charIndex - 1);//只画一行 + } + } + //上面的width没有减去padding-left,可能啥也没画 + return strBuff.toString(); + } + + /** + * 将输入字符串转换为实际字符,\代表转义字符。 + * + * @param text 字符串 + * @return the real value 实际字符 + */ + public static String textToString(String text) { + //跟这里的drawText算法是一致的,这样"所见即所得", + //如果把drawText的参数text先用此方法转换也行的,但是会遍历俩次,所以先留着 + if (text == null) { + return ""; + } + + int len = text.length(); + StringBuffer sb = new StringBuffer(len); + + for (int i = 0; i < len; i++) { + char c = text.charAt(i); + if (c == '\\' && i + 1 < len) { + char next = text.charAt(i + 1); + if (next == 'n') { + i++; + c = '\n'; + } else if (next == '\\') { + i++; + } + + } + sb.append(c); + } + return sb.toString(); + } + + /** + * daniel: 自动换行算法, 这个算法要求跟DHTML中Table的自动换行表现结果一样 + * 所以统一72dpi进行处理 + * + * @param text + * @param style + * @param paintWidth + * @return + */ + public static List getLineTextList(String text, Style style, Font font, double paintWidth, int resolution) { + List lineTextList = new ArrayList(); + if (text == null || text.length() <= 0) { + return lineTextList; + } + + style = style == null ? Style.DEFAULT_STYLE : style; + + if (style.getRotation() != 0) { + lineTextList.add(text); + return lineTextList; + } + + if (style.getTextStyle() != Style.TEXTSTYLE_WRAPTEXT) { + return lineTextListNotChangeLine(lineTextList, text); + } + // 自动换行 + else { + UNIT width = FU.valueOfPix((int) paintWidth, resolution); + return lineTextListAutoChangeLine(lineTextList, text, font, style, width, resolution); + } + } + + private static List lineTextListNotChangeLine(List lineTextList, String text) { + char tmpChar; + StringBuffer tmpTextBuf = new StringBuffer(); + for (int t = 0; t < text.length(); t++) { + tmpChar = text.charAt(t); + if (tmpChar == '\\') {// 判断是否是 "\n" + if (t + 1 < text.length() && text.charAt(t + 1) == 'n') { + // 是"\n"字符串,但不是换行符. + t++; + lineTextList.add(tmpTextBuf.toString()); + tmpTextBuf.delete(0, tmpTextBuf.length()); + } else { + tmpTextBuf.append(tmpChar); + } + } else { + tmpTextBuf.append(tmpChar); + } + } + + // 最后一个 + if (tmpTextBuf.length() > 0) { + lineTextList.add(tmpTextBuf.toString()); + tmpTextBuf.delete(0, tmpTextBuf.length()); + } + + return lineTextList; + } + + /** + * 这里需要用resolution 72的dpi进行分行,因为96dpi会导致font有小数导致获取的宽度不正确 + * + * @param lineTextList + * @param text + * @param style + * @return + */ + private static List lineTextListAutoChangeLine(List lineTextList, String text, Font font, Style style, UNIT unitWidth, int resolution) { + + AutoChangeLineAndDrawProcess process = AutoChangeLineAndDrawManager.getProcess(); + if (process != null) { + return process.autoChangeLine(text, new ObjectHolder(style), unitWidth); + } + if (font == null) { + font = style.getFRFont(); + } + FontMetrics fontMetrics = GraphHelper.getFontMetrics(font); + double paintWidth = unitWidth.toPixD(resolution); + double width = paintWidth - style.getPaddingLeft() - style.getPaddingRight() - style.getBorderLeftWidth(); + StringBuffer lineTextBuf = new StringBuffer(); + int lineTextWidth = 0; + + StringBuffer wordBuf = new StringBuffer(); + int wordWidth = 0; + int[] tmpWidth = new int[2]; + tmpWidth[0] = wordWidth; + tmpWidth[1] = lineTextWidth; + + lineTextListDealWithText(text, lineTextList, width, tmpWidth, wordBuf, lineTextBuf, fontMetrics); + + // 最后处理 + if (tmpWidth[1] + tmpWidth[0] > width && lineTextBuf.length() > 0) { + lineTextList.add(lineTextBuf.toString()); + lineTextList.add(wordBuf.toString()); + } else { + lineTextBuf.append(wordBuf); + lineTextList.add(lineTextBuf.toString()); + } + return lineTextList; + } + private static void lineTextListDealWithText(String text, List lineTextList, double width, int[] tmpWidth, + StringBuffer wordBuf, StringBuffer lineTextBuf, FontMetrics fontMetrics) { + for (int t = 0; t < text.length(); t++) { + if (t != 0 && isNumOrLetter(text.charAt(t)) && isNumOrLetter(text.charAt(t - 1))) { + dealWithTextNumOrLetter(text, t, lineTextList, width, tmpWidth, wordBuf, lineTextBuf, fontMetrics); + } else if (text.charAt(t) == '\n' || (text.charAt(t) == '\r' && t + 1 < text.length() - 1 && text.charAt(t + 1) != '\n')) { + dealWithTextChangeLineSymbol(text, t, lineTextList, width, tmpWidth, wordBuf, lineTextBuf, fontMetrics); + } else if (text.charAt(t) == '\\' && t + 1 < text.length() && text.charAt(t + 1) == 'n') {// 判断是否是 "\n" + // 是"\n"字符串,但不是换行符,依然需要换行. + dealWidthTextManualChangeLine(text, t, lineTextList, width, tmpWidth, wordBuf, lineTextBuf, fontMetrics); + // 需要跳过后面的n + t++; + } else { + int increaseStep = 0; + if (text.charAt(t) == '\\' && t + 1 < text.length() && text.charAt(t + 1) == '\\') {// 判断是否是转义字符'\' + // _denny: 增加了转义字符'\\'用来表示\,使"\n"可以输入 + t++; + increaseStep++; + } + if (tmpWidth[1] + tmpWidth[0] > width && lineTextBuf.length() > 0) { + if (isPunctuationAtLineHead(t, text)) { + for (int index = lineTextBuf.length(); index > 0; index--) { + char prec = lineTextBuf.charAt(index - 1); + lineTextBuf.deleteCharAt(index - 1); + if (!isPunctuation(prec)) { + break; + } + } + } + lineTextList.add(lineTextBuf.toString()); + lineTextBuf.delete(0, lineTextBuf.length()); + tmpWidth[1] = isPunctuationAtLineHead(t, text) ? dealWithPunctuationAtLinehead(t, fontMetrics, text, lineTextBuf, wordBuf.length(), increaseStep) : 0; + } + lineTextBuf.append(wordBuf); + tmpWidth[1] += tmpWidth[0]; + + wordBuf.delete(0, wordBuf.length()); + tmpWidth[0] = 0; + wordBuf.append(text.charAt(t)); + tmpWidth[0] = fontMetrics.charWidth(text.charAt(t)); + } + } + } + + + + /** + * 标点符号是否在换行后的行首 + */ + private static boolean isPunctuationAtLineHead(int t, String text) { + return t > 1 && BaseUtils.isPunctuation(text.charAt(t - 1)); + } + + private static int dealWithPunctuationAtLinehead(int t, FontMetrics fontMetrics, String text, StringBuffer lineTextBuf, int wordBufLength, int increaseStep) { + //lineTextBuf 最后一个字符下标 与 text当前字符下标 t 的距离 + 1 + int indexDistance = 1 + wordBufLength + increaseStep; + if (t < indexDistance) { + return 0; + } + int lineWidth = 0; + for (int index = t - indexDistance; index >= 0; index--) { + lineWidth += fontMetrics.charWidth(text.charAt(index)); + lineTextBuf.insert(0, text.charAt(index)); + if (!isPunctuation(text.charAt(index))) { + break; + } + } + return lineWidth; + } + + private static void dealWithTextNumOrLetter(String text, int t, List lineTextList, double width, int[] tmpWidth, + StringBuffer wordBuf, StringBuffer lineTextBuf, FontMetrics fontMetrics) { + if (tmpWidth[0] + fontMetrics.charWidth(text.charAt(t)) > width) { + if (tmpWidth[1] > 0) { + lineTextList.add(lineTextBuf.toString()); + lineTextBuf.delete(0, lineTextBuf.length()); + tmpWidth[1] = 0; + } + + lineTextList.add(wordBuf.toString()); + wordBuf.delete(0, wordBuf.length()); + tmpWidth[0] = 0; + } + + wordBuf.append(text.charAt(t)); + tmpWidth[0] += fontMetrics.charWidth(text.charAt(t)); + } + + private static void dealWithTextChangeLineSymbol(String text, int t, List lineTextList, double width, int[] tmpWidth, + StringBuffer wordBuf, StringBuffer lineTextBuf, FontMetrics fontMetrics) { + wordBuf.append('\n'); + if (tmpWidth[1] + tmpWidth[0] > width && lineTextBuf.length() > 0) { + lineTextList.add(lineTextBuf.toString()); + lineTextList.add(wordBuf.toString()); + } else { + lineTextBuf.append(wordBuf); + lineTextList.add(lineTextBuf.toString()); + } + lineTextBuf.delete(0, lineTextBuf.length()); + tmpWidth[1] = 0; + wordBuf.delete(0, wordBuf.length()); + tmpWidth[0] = 0; + } + + private static void dealWidthTextManualChangeLine(String text, int t, List lineTextList, double width, int[] tmpWidth, + StringBuffer wordBuf, StringBuffer lineTextBuf, FontMetrics fontMetrics) { + t++;// 忽略'n'字符. + wordBuf.append('\n'); + if (tmpWidth[1] + tmpWidth[0] > width && lineTextBuf.length() > 0) { + lineTextList.add(lineTextBuf.toString()); + lineTextList.add(wordBuf.toString()); + } else { + lineTextBuf.append(wordBuf); + lineTextList.add(lineTextBuf.toString()); + } + lineTextBuf.delete(0, lineTextBuf.length()); + tmpWidth[1] = 0; + wordBuf.delete(0, wordBuf.length()); + tmpWidth[0] = 0; + } + + + /** + * @param cuChar + * @return + * @see StableUtils + * @deprecated + */ + public static boolean isNum(char cuChar) { + return StableUtils.isNum(cuChar); + } + + + /** + * 判断字符是否为数字或字母 + * + * @param curChar 被检查的字符 + * @return 是否为数字或字母 + */ + public static boolean isNumOrLetter(char curChar) { + return GeneralUtils.isLetter(curChar) || StableUtils.isNum(curChar); + } + + public static boolean isPunctuation(char c) { + int type = Character.getType(c); + return type == Character.OTHER_PUNCTUATION + || type == Character.DASH_PUNCTUATION + || type == Character.START_PUNCTUATION + || type == Character.END_PUNCTUATION + || type == Character.CONNECTOR_PUNCTUATION; + } + + + public static List getLineTextList(String text, Style style, Font font, double paintHeight, double paintWidth) { + return getLineTextList(text, style, font, paintHeight, paintWidth, Constants.DEFAULT_PRINT_AND_EXPORT_RESOLUTION); + } + + /** + * james daniel 放一起 + * 同时含有height和width参数表示会根据style自动判断字体为竖排还是横排 + * TODO + */ + public static List getLineTextList(String text, Style style, Font font, double paintHeight, double paintWidth, int resolution) { + //正常文字 + if (style == null + || style.getVerticalText() != Style.VERTICALTEXT) {//james:正常的文字时 + return BaseUtils.getLineTextList(text, style, font, paintWidth, resolution); + } + ExtraClassManagerProvider pluginProvider = PluginModule.getAgent(PluginModule.ExtraCore); + if (pluginProvider != null) { + VerticalTextProcessor processor = pluginProvider.getSingle(VerticalTextProcessor.XML_TAG, DefaultVerticalTextProcessor.class); + String[] res = processor.process(text, style, font, paintHeight, paintWidth, resolution); + return Arrays.asList(res); + } + String[] res = new DefaultVerticalTextProcessor().process(text, style, font, paintHeight, paintWidth, resolution); + return Arrays.asList(res); + } + + /** + * 返回边框宽 + * + * @param borderType 边框类型 + * @return 宽度 + */ + public static int getBorderWidth(int borderType) { + switch (borderType) { + case Constants.LINE_NONE: + return 0; + case Constants.LINE_SLIM: + return 1; + case Constants.LINE_THIN: + return 1; + case Constants.LINE_DASH: + return 1; + case Constants.LINE_HAIR: + return 1; + case Constants.LINE_HAIR2: + return 1; + case Constants.LINE_THICK: + return 3; + case Constants.LINE_DOT: + return 1; + default: + return 2; + } + } + + /** + * 将边框转为对应的字符串描述 + * + * @param borderStyle 边框线型 + * @return web端对应的边框 + */ + public static String border2Style(int borderStyle) { + switch (borderStyle) { + case Constants.LINE_NONE: + return "none"; + case Constants.LINE_SLIM: + return "solid"; + case Constants.LINE_THIN: + return "solid"; + case Constants.LINE_MEDIUM: + return "solid"; + case Constants.LINE_THICK: + return "solid"; + case Constants.LINE_DOUBLE: + return "double"; + case Constants.LINE_DOT: + return "double"; + case Constants.LINE_DASH_DOT: + return "double"; + case Constants.LINE_DASH_DOT_DOT: + return "dotted"; + default: + return "dashed"; + } + } + + /** + * 克隆对象 + * + * @param object 对象 + * @return 克隆出的对象 + * @throws CloneNotSupportedException 不被支持的克隆异常 + * @see StableUtils#cloneObject(Object) + * @deprecated + */ + public static Object cloneObject(Object object) throws CloneNotSupportedException { + return StableUtils.cloneObject(object); + } + + + /** + * 把java.util.Map转成一段JSONObject的String与上面方法对应 + * + * @param map map对象 + * @return json对象 + * @throws JSONException json异常 + */ + public static JSONObject map2JSON(Map map) throws JSONException { + JSONObject jo = new JSONObject(); + Iterator iter = map.keySet().iterator(); + while (iter.hasNext()) { + Map.Entry entry = (Map.Entry) iter.next(); + Object value = entry.getValue(); + if (value instanceof FArray) { + JSONArray ja = new JSONArray(); + FArray array = (FArray) value; + for (int i = 0; i < array.length(); i++) { + ja.put(array.elementAt(i)); + } + value = ja; + } + jo.put((String) entry.getKey(), value); + } + return jo; + } + + /** + * 将一个对象转化成json样式的字符串 + * + * @param o 待转化的对象 + * @return json样式的字符串 + * @throws JSONException json异常 + * @see EmbedJson#encode(Object) (Object) + * @deprecated + */ + public static String jsonEncode(Object o) throws JSONException { + return EmbedJson.encode(o); + } + + + /** + * 将一个字符串转化成JSON格式的对象 + * + * @param str 要转化的字符串 + * @return JSON格式的对象 + * @throws JSONException json异常 + * @see JSONUtils#jsonDecode(String) + * @deprecated + */ + public static Object jsonDecode(String str) throws JSONException { + return JSONUtils.jsonDecode(str); + } + + /** + * 检查图片1和2是否相同. + * + * @param img1 图片1 + * @param img2 图片2 + * @return 图片1和2是否相同 + */ + public static boolean imageEquals(Image img1, Image img2) { + if (img1 == img2) { + return true; + } + + if (img1 == null || img2 == null) {//null < not null + return img1 == null && img2 == null; + } + + //开始比较图片. + byte[] buf1 = GeneralXMLTools.imageEncode(img1); + byte[] buf2 = GeneralXMLTools.imageEncode(img2); + if (buf1.length != buf2.length) { + return false; + } + + for (int i = 0; i < buf1.length; i++) { + if (buf1[i] != buf2[i]) { + return false; + } + } + + return true; + } + + /** + * 根据数据集名获取数据模型 + * + * @param cal 算子 + * @param tdName 列表名 + * @return 数据模型 + */ + public static DataModel getDataModelFromTableDataName(Calculator cal, String tdName) { + String tableDataName = tdName; + //先看当前的报表中的私有数据源 + DataModel resultSet = SynchronizedLiveDataModelUtils.getSELiveDataModel4Share(cal, tableDataName); + if (resultSet == null) { + TableData td = TableDataAssist.getTableData(cal, tableDataName); + resultSet = td == null ? null : td.createDataModel(cal); + } + return resultSet; + } + + + /** + * kunsnat: List转化为二维表 + * + * @param list 列表 + * @return 二维数组 + */ + public static Object[][] list2Array2D(List list) { + int rowNum = list.size(); + int colNum = 0; + for (int i = 0; i < rowNum; i++) { + List row = (List) list.get(i); + if (row.size() > colNum) { + colNum = row.size(); + } + } + Object[][] result = new Object[rowNum][colNum]; + + for (int i = 0; i < rowNum; i++) { + List row = (List) list.get(i); + for (int j = 0; j < colNum; j++) { + if (j < row.size()) { + result[i][j] = row.get(j); + } else { + result[i][j] = null; + } + } + } + + return result; + } + + /** + * 判断当前Env下是否有lic文件 + * + * @return 判断当前Env下是否有lic文件 + */ + public static boolean checkLicExist() { + + if (ServerPreferenceConfig.getInstance().isLicUseLock()) { + return true;//加密狗 + } else if (!LicenseConfigManager.getInstance().isUseFile()) { + //不使用文件,说明用户配置了注册信息,一定是注册了 + return true; + } + + try { + String licName = FRContext.getCommonOperator().getLicenseName(); + byte[] bytes = WorkContext.getWorkResource().readFully(StableUtils.pathJoin(ProjectConstants.RESOURCES_NAME, licName)); + return bytes != null && bytes.length != 0; + } catch (Exception ignored) { + } + + return false; + } + + /** + * 判断是否内容为图表相关. + * + * @param cellOption 单元格属性 + * @return 返回是否为图表单元格相关. + */ + public static boolean isChartCell(JSONObject cellOption) { + if (cellOption != null) { + JSONObject value = cellOption.optJSONObject("value"); + if (value != null) { + return ComparatorUtils.equals("simplechart", value.optString("type")); + } + } + + return false; + } +} diff --git a/designer-form/src/main/java/com/fr/design/fit/common/FormDesignerUtil.java b/designer-form/src/main/java/com/fr/design/fit/common/FormDesignerUtil.java new file mode 100644 index 000000000..6e0739a01 --- /dev/null +++ b/designer-form/src/main/java/com/fr/design/fit/common/FormDesignerUtil.java @@ -0,0 +1,45 @@ +package com.fr.design.fit.common; + +import com.fr.design.designer.creator.XCreator; +import com.fr.design.designer.creator.XElementCase; +import com.fr.design.designer.creator.XLayoutContainer; +import com.fr.form.FormElementCaseProvider; +import com.fr.form.ui.ElementCaseEditor; + +import java.util.Iterator; +import java.util.List; + +/** + * Created by kerry on 2020-06-12 + */ +public class FormDesignerUtil { + private FormDesignerUtil(){ + + } + public static XElementCase getXelementCase(XLayoutContainer rootContainer, FormElementCaseProvider elementCaseProvider) { + for (int i = 0; i < rootContainer.getComponentCount(); i++) { + XCreator creator = rootContainer.getXCreator(i); + if (creator instanceof XElementCase && elementCaseProvider == ((ElementCaseEditor) creator.toData()).getElementCase()) { + return (XElementCase) creator; + } + if (creator instanceof XLayoutContainer) { + XElementCase temp = getXelementCase((XLayoutContainer) creator, elementCaseProvider); + if (temp != null) { + return temp; + } + } + } + return null; + } + + + public static void removeDeletedEC(List currentECList, List waitToProcessECList) { + Iterator iterator = waitToProcessECList.iterator(); + while (iterator.hasNext()) { + Object editor = iterator.next(); + if (!currentECList.contains(editor)) { + iterator.remove(); + } + } + } +} diff --git a/designer-form/src/main/java/com/fr/design/fit/common/LayoutTool.java b/designer-form/src/main/java/com/fr/design/fit/common/LayoutTool.java new file mode 100644 index 000000000..6bebbbbe8 --- /dev/null +++ b/designer-form/src/main/java/com/fr/design/fit/common/LayoutTool.java @@ -0,0 +1,177 @@ +package com.fr.design.fit.common; + +import com.fr.design.designer.creator.XCreator; +import com.fr.design.designer.creator.XLayoutContainer; +import com.fr.design.designer.creator.XWAbsoluteBodyLayout; +import com.fr.design.designer.creator.XWAbsoluteLayout; +import com.fr.design.designer.creator.cardlayout.XWCardLayout; +import com.fr.design.designer.creator.cardlayout.XWCardMainBorderLayout; +import com.fr.design.fit.NewJForm; +import com.fr.design.mainframe.FormArea; +import com.fr.design.mainframe.JForm; +import com.fr.design.utils.gui.LayoutUtils; +import com.fr.form.ui.container.WAbsoluteBodyLayout; +import com.fr.form.ui.container.WFitLayout; +import com.fr.form.ui.container.WLayout; +import com.fr.form.ui.widget.CRBoundsWidget; +import com.fr.general.FRScreen; +import com.fr.log.FineLoggerFactory; + +import java.awt.Component; +import java.awt.Dimension; +import java.awt.Rectangle; +import java.awt.Toolkit; + +/** + * @description:布局工具类 + * @author: Henry.Wang + * @create: 2020/09/03 14:18 + */ +public class LayoutTool { + /** + * @Description: 获取布局类型 + * @param jForm + * @return: + * @Author: Henry.Wang + * @date: 2020/9/6 14:31 + */ + public static int getFormLayoutType(JForm jForm) { + JForm tempJForm = jForm; + if (tempJForm == null) + tempJForm = TemplateTool.getCurrentEditingNewJForm(); + if (tempJForm == null || tempJForm.getFormDesign().getRootComponent() == null) + return -1; + return ((WFitLayout) tempJForm.getFormDesign().getRootComponent().toData()).getBodyLayoutType().getTypeValue(); + + } + + /** + * @Description: 是否为绝对布局 + * @param jForm + * @return: + * @Author: Henry.Wang + * @date: 2020/9/6 14:31 + */ + public static boolean absoluteLayoutForm(NewJForm jForm) { + if (getFormLayoutType(jForm) == 1) + return true; + return false; + } + + /** + * @Description: 如果为绝对布局则获取XWAbsoluteBodyLayout,否则返回null + * @param jForm + * @return: + * @Author: Henry.Wang + * @date: 2020/9/6 14:26 + */ + public static XWAbsoluteBodyLayout getXWAbsoluteBodyLayout(NewJForm jForm) { + NewJForm tempJForm = jForm; + if (tempJForm == null) + tempJForm = TemplateTool.getCurrentEditingNewJForm(); + if (tempJForm != null) { + if (absoluteLayoutForm(tempJForm)) { + XLayoutContainer xLayoutContainer = tempJForm.getFormDesign().getRootComponent(); + if (xLayoutContainer != null && xLayoutContainer.getComponentCount() > 0) { + Component component = xLayoutContainer.getComponent(0); + if (component instanceof XWAbsoluteBodyLayout) { + return (XWAbsoluteBodyLayout) component; + } + } + } + } + return null; + } + + /** + * @Description: 通过子组件的位置获取绝对布局body的尺寸。为什么不直接调用getRootComponent().toData().getContentHeight()呢?因为在截断的情况下就不不一致了 + * @param newJForm + * @return: + * @Author: Henry.Wang + * @date: 2020/9/11 15:24 + */ + public static Rectangle getAbsoluteBodySize(NewJForm newJForm) { + int width = 0; + int height = 0; + XWAbsoluteBodyLayout xwAbsoluteBodyLayout = LayoutTool.getXWAbsoluteBodyLayout(newJForm); + if (xwAbsoluteBodyLayout != null) { + WAbsoluteBodyLayout wAbsoluteBodyLayout = xwAbsoluteBodyLayout.toData(); + if (wAbsoluteBodyLayout.getWidgetCount() > 0) { + for (int i = 0; i < wAbsoluteBodyLayout.getWidgetCount(); i++) { + Rectangle rectangle = ((CRBoundsWidget) wAbsoluteBodyLayout.getWidget(i)).getBounds(); + if (rectangle.x + rectangle.width > width) + width = rectangle.x + rectangle.width; + if (rectangle.y + rectangle.height > height) + height = rectangle.y + rectangle.height; + } + } + } + return new Rectangle(0, 0, width, height); + } + + + public static int getBodyHeight(NewJForm newJForm) { + return newJForm.getFormDesign().getRootComponent().toData().getContentHeight(); + } + + public static int getBodyWidth(NewJForm newJForm) { + return newJForm.getFormDesign().getRootComponent().toData().getContentWidth(); + } + + /** + * @Description: 对绝对画布块里的组件进行缩放 + * @param xCreator + * @param scale 缩放比例 + * @return: + * @Author: Henry.Wang + * @date: 2020/10/26 20:39 + */ + public static void scaleAbsoluteBlockComponentsBounds(XCreator xCreator, Double scale) { + try { + Component[] components = xCreator.getComponents(); + for (Component component : components) { + if (component instanceof XWCardMainBorderLayout) { + XWCardLayout xwCardLayout = ((XWCardMainBorderLayout)component).getCardPart(); + for (Component tabComponent : xwCardLayout.getComponents()) { + scaleAbsoluteBlockComponentsBounds((XCreator) tabComponent, scale); + scaleAbsoluteBlockComponentsBoundsInternal((XCreator) tabComponent, scale); + } + } else if (component instanceof XWAbsoluteLayout) { + scaleAbsoluteBlockComponentsBounds((XCreator) component, scale); + scaleAbsoluteBlockComponentsBoundsInternal((XCreator) component, scale); + } + } + } catch (Exception e) { + FineLoggerFactory.getLogger().error(e.getMessage()); + } + + } + + public static void scaleAbsoluteBlockComponentsBoundsInternal(XCreator parentXCreator, Double scale) { + if (parentXCreator instanceof XWAbsoluteLayout) { + XWAbsoluteLayout xwAbsoluteLayout = (XWAbsoluteLayout) parentXCreator; + Component[] absoluteComponents = xwAbsoluteLayout.getComponents(); + for (Component absoluteComponent : absoluteComponents) { + XCreator xCreator = (XCreator) absoluteComponent; + Rectangle bounds = new Rectangle(absoluteComponent.getBounds()); + Rectangle newBounds = new Rectangle((int) (bounds.x * scale), (int) (bounds.y * scale), (int) (bounds.width * scale), (int) (bounds.height * scale)); + WLayout wAbsoluteLayout = xwAbsoluteLayout.toData(); + wAbsoluteLayout.setBounds(xCreator.toData(), newBounds); + xCreator.setBounds(newBounds); + LayoutUtils.layoutContainer(xCreator); + } + } + } + + /** + * @Description: 获取老预览时的缩放比例 + * @return: + * @Author: Henry.Wang + * @date: 2020/10/26 20:39 + */ + public static double getContainerPercent(){ + Dimension screen = Toolkit.getDefaultToolkit().getScreenSize(); + double screenValue = FRScreen.getFRScreenByDimension(screen).getValue(); + return screenValue / FormArea.DEFAULT_SLIDER; + } +} diff --git a/designer-form/src/main/java/com/fr/design/fit/common/NewFormStyle.java b/designer-form/src/main/java/com/fr/design/fit/common/NewFormStyle.java new file mode 100644 index 000000000..a937ba2d4 --- /dev/null +++ b/designer-form/src/main/java/com/fr/design/fit/common/NewFormStyle.java @@ -0,0 +1,2030 @@ +/* + * Copyright(c) 2001-2010, FineReport Inc, All Rights Reserved. + */ +package com.fr.design.fit.common; + +import com.fr.base.BaseFormula; +import com.fr.base.CoreDecimalFormat; +import com.fr.base.FRContext; +import com.fr.base.FormatRepository; +import com.fr.base.GraphHelper; +import com.fr.base.ImageProvider; +import com.fr.base.Painter; +import com.fr.base.Style; +import com.fr.base.Utils; +import com.fr.base.background.ImageBackground; +import com.fr.common.annotations.Open; +import com.fr.data.DataBaseUtils; +import com.fr.general.Background; +import com.fr.general.ComparatorUtils; +import com.fr.general.DateUtils; +import com.fr.general.DefaultValues; +import com.fr.general.FRFont; +import com.fr.general.GeneralUtils; +import com.fr.json.JSONException; +import com.fr.json.JSONObject; +import com.fr.stable.AssistUtils; +import com.fr.stable.Constants; +import com.fr.stable.CoreGraphHelper; +import com.fr.stable.StableUtils; +import com.fr.stable.StringUtils; +import com.fr.stable.unit.PT; +import com.fr.stable.web.Repository; +import com.fr.stable.web.ServletContext; +import com.fr.stable.web.ServletContextAdapter; +import org.jetbrains.annotations.Nullable; + +import java.awt.Color; +import java.awt.Dimension; +import java.awt.Font; +import java.awt.FontMetrics; +import java.awt.Graphics2D; +import java.awt.Image; +import java.awt.Paint; +import java.awt.Rectangle; +import java.awt.Shape; +import java.awt.geom.Rectangle2D; +import java.io.InvalidObjectException; +import java.io.Serializable; +import java.sql.Clob; +import java.text.DateFormat; +import java.text.Format; +import java.text.NumberFormat; +import java.util.ArrayList; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +/** + * 用于表示样式的类,包括边框、颜色、字体、缩进、间距等 + * 注意:这是一个开放类,所有的修改都要注意保持兼容 + */ +@Open +public class NewFormStyle extends Style implements Serializable, Cloneable { + + // temp rectangle + private static Rectangle2D tempRectangle2D = new Rectangle2D.Double(0, 0, 0, 0); + + public static final int TEXTSTYLE_WRAPTEXT = 0; // 换行. + public static final int TEXTSTYLE_SINGLELINE = 1; // 单行,超出部分不显示. + public static final int TEXTSTYLE_SINGLELINEADJUSTFONT = 2; // 单行,调整字体来显示所有的字. + public static final int TEXTSTYLE_MULTILINEADJUSTFONT = 3; //多行,固定单元格高和宽,调整字体来显示所有的字 + + public static final int VERTICALTEXT = 1;// 竖排,与Excel中的设置值相同 + public static final int HORIZONTALTEXT = 0;// 横排 + + public static final int LEFT_TO_RIGHT = 1;// JAMES:垂直文字时,文字为从左向右顺序排列 + public static final int RIGHT_TO_LEFT = 0;// JAMES:垂直文字时,文字为从右向左顺序排列 + + //EXCEL 宋体11号字, 一个缩进单位对应96dpi下27px + public static final int PADDING_ARG = 27; + public static final int DEFAULT_PADDING = 2; + /** + * 细线透明度值 + */ + public static final double FACTORY_ALPHA = 0.4; + public static final NewFormStyle NULL_STYLE = null; + + /** + * Default Style + */ + public static final NewFormStyle DEFAULT_STYLE = new NewFormStyle(); + + /** + * Border Style + */ + public static final NewFormStyle BORDER_STYLE = new NewFormStyle(); + + private static final long serialVersionUID = -1675056857456167839L; + private static final int MAX_FONT_SIZE = 100; + private static final int N_SIZE = 9; + private static final int MIN_SIZE = 8; + private static final int MAX_SIZE = 26; + + private static final int LINE_HEIGHT_FIX = 4; + private static final int LINE_HEIGHT_FIX2 = 3; + + private static class ContentLineGroup { + Style style; + List newLineTextList; + int newTextHeight; + + public ContentLineGroup(Style style, List lineList, int textHeight) { + this.style = style; + this.newLineTextList = lineList; + this.newTextHeight = textHeight; + } + + public int totalHeight() { + return newTextHeight * newLineTextList.size(); + } + } + + private static Map clsFontFamily = null; + + // 全局Style的map + private static Map initializeStyle = new ConcurrentHashMap();// + + static { + ServletContext.addServletContextListener(new ServletContextAdapter() { + @Override + public void onServletStop() { + initializeStyle.clear(); + } + }); + } + + // format + private Format format = null; + + // element font. + private FRFont frFont = null; + + private Background background = null; + + // border. + private byte border_top = Constants.LINE_NONE; + private byte border_left = Constants.LINE_NONE; + private byte border_bottom = Constants.LINE_NONE; + private byte border_right = Constants.LINE_NONE; + private Color border_top_color = Color.BLACK; + private Color border_left_color = Color.BLACK; + private Color border_bottom_color = Color.BLACK; + private Color border_right_color = Color.BLACK; + + // alignment. + private int horizontal_alignment = Constants.NULL; + private int vertical_alignment = Constants.CENTER; + + // TextStyle + private int textStyle = NewFormStyle.TEXTSTYLE_WRAPTEXT; + + // james: verticalText + private int verticalText = NewFormStyle.HORIZONTALTEXT;// 默认为0,水平方向 + + // james: the direction of text when the text is vertical + private int textDirection = NewFormStyle.RIGHT_TO_LEFT;// 默认为从右向左的顺序 + + private int rotation = 0; + + private byte imageLayout = Constants.IMAGE_CENTER;//现在默认改为center了 + + //padding有两个计量单位, 一种是以前的pt, 一种是现在的, 为了兼容excel的 + //设计器中设置1个单位, 兼容为以前的27pt + // 左缩进 + private int paddingLeft = DEFAULT_PADDING; + // 右缩进 + private int paddingRight = DEFAULT_PADDING; + + // 段前间距 + private byte spacingBefore = 0; + // 段后间距 + private byte spacingAfter = 0; + // 行间距h + private byte lineSpacing = 0; + + private String contentClsCss = StringUtils.EMPTY; + private Map contentStyleCssMap = new HashMap(); + + private String borderClsCss = StringUtils.EMPTY; + private Map borderStyleCssMap = new HashMap(); + + /** + * Constructor. + */ + protected NewFormStyle() { + //peter:这里主动从Context, 获得默认的FRFont的值. + DefaultValues defaultValues = FRContext.getDefaultValues(); + this.frFont = defaultValues.getFRFont(); + + // HTML输出 CSS初始化 + this.contentClsCss = this.contentStyle2class(this.contentStyleCssMap); + this.borderClsCss = this.border2Class(this.borderStyleCssMap); + } + + private NewFormStyle(Background background, Format format, FRFont frFont, + int border_top, Color border_top_color, int border_bottom, Color border_bottom_color, + int border_left, Color border_left_color, int border_right, Color border_right_color, + int horizontal_alignment, int vertical_alignment, int textStyle, + int verticalText, int textDirection, int rotation, int imageLayout, + int paddingLeft, int paddingRight, int spacingBefore, int spacingAfter, int lineSpacing) { + this.background = background; + this.format = format; + this.frFont = frFont; + + this.border_top = (byte) border_top; + this.border_top_color = border_top_color; + this.border_bottom = (byte) border_bottom; + this.border_bottom_color = border_bottom_color; + this.border_left = (byte) border_left; + this.border_left_color = border_left_color; + this.border_right = (byte) border_right; + this.border_right_color = border_right_color; + + this.horizontal_alignment = (byte) horizontal_alignment; + this.vertical_alignment = (byte) vertical_alignment; + this.textStyle = (byte) textStyle; + + this.verticalText = (byte) verticalText; + this.textDirection = (byte) textDirection; + this.rotation = (short) rotation; + this.imageLayout = (byte) imageLayout; + + this.paddingLeft = paddingLeft; + this.paddingRight = paddingRight; + + this.spacingBefore = (byte) spacingBefore; + this.spacingAfter = (byte) spacingAfter; + this.lineSpacing = (byte) lineSpacing; + + // HTML输出 CSS初始化 + this.contentClsCss = this.contentStyle2class(this.contentStyleCssMap); + this.borderClsCss = this.border2Class(this.borderStyleCssMap); + } + + private static Map getclsFontFamily() { + if (clsFontFamily == null) { + clsFontFamily = new HashMap() { + { + put("Arial Black", "fnab"); + put("Basic Sans SF", "fnbs"); + put("Book Antiqua", "fnba"); + put("Calibri", "fnci"); + put("Comic Sans MS", "fncs"); + put("Courier New", "fncn"); + put("Elementary SF", "fnes"); + put("Garamond", "fngd"); + put("Georgia", "fnga"); + put("Letter Gothic", "fnlg"); + put("Lucida Console", "fnlc"); + put("Marigold", "fnmd"); + put("MS Sans Serif", "fnms"); + put("MS Gothic", "fnmg"); + put("MS PGothic", "fnmpg"); + put("MS Mincho", "fnmm"); + put("MS P Mincho", "fnmpm"); + put("MingLiU", "fnml"); + put("SimHei", "fnsh"); + put("System", "fnsm"); + put("Tahoma", "fnta"); + put("Times New Roman", "fntn"); + put("Trebuchet MS", "fntms"); + put("Verdana", "fnva"); + + put("SimSun", "fnss"); + put("Arial", "fnar"); + put("Microsoft YaHei", "fnyh"); + put("KaiTi", "fnkt"); + } + }; + } + + return clsFontFamily; + } + + /** + * 设置样式 + * + * @param format 格式g + * @return 样式y + */ + public NewFormStyle deriveFormat(Format format) { + return getInstance(background, format, frFont, + border_top, border_top_color, border_bottom, border_bottom_color, + border_left, border_left_color, border_right, border_right_color, + horizontal_alignment, vertical_alignment, textStyle, + verticalText, textDirection, rotation, imageLayout, + paddingLeft, paddingRight, spacingBefore, spacingAfter, lineSpacing); + } + + /** + * 获取实例 + */ + public static NewFormStyle getInstance(Format format) { + return DEFAULT_STYLE.deriveFormat(format); + } + + /** + * 设置字体 + * + * @param frFont 字体 + * @return 样式 + */ + public NewFormStyle deriveFRFont(FRFont frFont) { + return getInstance(background, format, frFont, + border_top, border_top_color, border_bottom, border_bottom_color, + border_left, border_left_color, border_right, border_right_color, + horizontal_alignment, vertical_alignment, textStyle, + verticalText, textDirection, rotation, imageLayout, + paddingLeft, paddingRight, spacingBefore, spacingAfter, lineSpacing); + } + + /** + * getInstance + */ + public static NewFormStyle getInstance() { + NewFormStyle style = new NewFormStyle(); + + Object valueStyle = initializeStyle.get(style); + if (valueStyle != null) { + return (NewFormStyle) valueStyle; + } else { + initializeStyle.put(style, style); + return style; + } + } + + /** + * getInstance + */ + private static NewFormStyle getInstance(Background background, Format format, FRFont frFont, + int border_top, Color border_top_color, int border_bottom, Color border_bottom_color, + int border_left, Color border_left_color, int border_right, Color border_right_color, + int horizontal_alignment, int vertical_alignment, int textStyle, + int verticalText, int textDirection, int rotation, int imageLayout, + int paddingLeft, int paddingRight, int spacingBefore, int spacingAfter, int lineSpacing) { + NewFormStyle style = new NewFormStyle(background, format, frFont, + border_top, border_top_color, border_bottom, border_bottom_color, + border_left, border_left_color, border_right, border_right_color, + horizontal_alignment, vertical_alignment, textStyle, + verticalText, textDirection, rotation, imageLayout, + paddingLeft, paddingRight, spacingBefore, spacingAfter, lineSpacing); + + Object valueStyle = initializeStyle.get(style); + if (valueStyle != null) { + return (NewFormStyle) valueStyle; + } else { + initializeStyle.put(style, style); + return style; + } + } + + /** + * getInstance + */ + public static NewFormStyle getInstance(FRFont frFont) { + if (frFont == null) { + return NewFormStyle.getInstance(); + } + return DEFAULT_STYLE.deriveFRFont(frFont); + } + + /** + * 设置背景 + * + * @param background 背景 b + * @return 样式 y + */ + public NewFormStyle deriveBackground(Background background) { + if (background instanceof ImageBackground) { + return new NewFormStyle(background, format, frFont, + border_top, border_top_color, border_bottom, border_bottom_color, + border_left, border_left_color, border_right, border_right_color, + horizontal_alignment, vertical_alignment, textStyle, + verticalText, textDirection, rotation, imageLayout, + paddingLeft, paddingRight, spacingBefore, spacingAfter, lineSpacing); + } + + return getInstance(background, format, frFont, + border_top, border_top_color, border_bottom, border_bottom_color, + border_left, border_left_color, border_right, border_right_color, + horizontal_alignment, vertical_alignment, textStyle, + verticalText, textDirection, rotation, imageLayout, + paddingLeft, paddingRight, spacingBefore, spacingAfter, lineSpacing); + } + + /** + * getInstance + */ + public static NewFormStyle getInstance(Background background) { + return DEFAULT_STYLE.deriveBackground(background); + } + + /** + * 去掉边框和单元格背景 + * + * @return 样式y + */ + public NewFormStyle deriveBorderBackgroundNone() { + return getInstance(null, format, frFont, + Constants.LINE_NONE, border_top_color, Constants.LINE_NONE, border_bottom_color, + Constants.LINE_NONE, border_left_color, Constants.LINE_NONE, border_right_color, + horizontal_alignment, vertical_alignment, textStyle, + verticalText, textDirection, rotation, imageLayout, + paddingLeft, paddingRight, spacingBefore, spacingAfter, lineSpacing); + } + + /** + * 设置边框 + * + * @param border_top 上边框像素 + * @param border_top_color 上边框颜色s + * @param border_bottom 下 x + * @param border_bottom_color 下 x + * @param border_left 左 z + * @param border_left_color 左z + * @param border_right 右y + * @param border_right_color 右y + * @return 样式y + */ + public NewFormStyle deriveBorder(int border_top, Color border_top_color, + int border_bottom, Color border_bottom_color, + int border_left, Color border_left_color, + int border_right, Color border_right_color) { + return getInstance(background, format, frFont, + border_top, border_top_color, border_bottom, border_bottom_color, + border_left, border_left_color, border_right, border_right_color, + horizontal_alignment, vertical_alignment, textStyle, + verticalText, textDirection, rotation, imageLayout, + paddingLeft, paddingRight, spacingBefore, spacingAfter, lineSpacing); + } + + /** + * 设置上边框 + * + * @param border_top 上边框尺寸s + * @param border_top_color 上边框颜色 s + * @return 样式 y + */ + public NewFormStyle deriveBorderTop(int border_top, Color border_top_color) { + return getInstance(background, format, frFont, + border_top, border_top_color, border_bottom, border_bottom_color, + border_left, border_left_color, border_right, border_right_color, + horizontal_alignment, vertical_alignment, textStyle, + verticalText, textDirection, rotation, imageLayout, + paddingLeft, paddingRight, spacingBefore, spacingAfter, lineSpacing); + } + + /** + * 设置下边框 + * + * @param border_bottom 下边框尺寸 x + * @param border_bottom_color 下颜色x + * @return 样式y + */ + public NewFormStyle deriveBorderBottom(int border_bottom, Color border_bottom_color) { + return getInstance(background, format, frFont, + border_top, border_top_color, border_bottom, border_bottom_color, + border_left, border_left_color, border_right, border_right_color, + horizontal_alignment, vertical_alignment, textStyle, + verticalText, textDirection, rotation, imageLayout, + paddingLeft, paddingRight, spacingBefore, spacingAfter, lineSpacing); + } + + /** + * 设置左边框 + * + * @param border_left 尺寸 z + * @param border_left_color 颜色y + * @return 样式y + */ + public NewFormStyle deriveBorderLeft(int border_left, Color border_left_color) { + return getInstance(background, format, frFont, + border_top, border_top_color, border_bottom, border_bottom_color, + border_left, border_left_color, border_right, border_right_color, + horizontal_alignment, vertical_alignment, textStyle, + verticalText, textDirection, rotation, imageLayout, + paddingLeft, paddingRight, spacingBefore, spacingAfter, lineSpacing); + } + + /** + * 设置右边框 + * + * @param border_right 尺寸 c + * @param border_right_color 颜色 y + * @return 样式 y + */ + public NewFormStyle deriveBorderRight(int border_right, Color border_right_color) { + return getInstance(background, format, frFont, + border_top, border_top_color, border_bottom, border_bottom_color, + border_left, border_left_color, border_right, border_right_color, + horizontal_alignment, vertical_alignment, textStyle, + verticalText, textDirection, rotation, imageLayout, + paddingLeft, paddingRight, spacingBefore, spacingAfter, lineSpacing); + } + + /** + * 设置水平方向 + * + * @param horizontal_alignment 水平方向s + * @return 样式 y + */ + public NewFormStyle deriveHorizontalAlignment(int horizontal_alignment) { + return getInstance(background, format, frFont, + border_top, border_top_color, border_bottom, border_bottom_color, + border_left, border_left_color, border_right, border_right_color, + horizontal_alignment, vertical_alignment, textStyle, + verticalText, textDirection, rotation, imageLayout, + paddingLeft, paddingRight, spacingBefore, spacingAfter, lineSpacing); + } + + /** + * 设置垂直方向 + * + * @param vertical_alignment 垂直方向c + * @return 样式 + * @see Constants#TOP + * @see Constants#CENTER + * @see Constants#BOTTOM + */ + public NewFormStyle deriveVerticalAlignment(int vertical_alignment) { + return getInstance(background, format, frFont, + border_top, border_top_color, border_bottom, border_bottom_color, + border_left, border_left_color, border_right, border_right_color, + horizontal_alignment, vertical_alignment, textStyle, + verticalText, textDirection, rotation, imageLayout, + paddingLeft, paddingRight, spacingBefore, spacingAfter, lineSpacing); + } + + /** + * 设置单元格展示样式(自动换行, 单行显示, 多行显示等) + * + * @param textStyle 文字样式 + * @return 样式 + * @see NewFormStyle#TEXTSTYLE_WRAPTEXT + * @see NewFormStyle#TEXTSTYLE_SINGLELINE + * @see NewFormStyle#TEXTSTYLE_SINGLELINEADJUSTFONT + * @see NewFormStyle#TEXTSTYLE_MULTILINEADJUSTFONT + */ + public NewFormStyle deriveTextStyle(int textStyle) { + return getInstance(background, format, frFont, + border_top, border_top_color, border_bottom, border_bottom_color, + border_left, border_left_color, border_right, border_right_color, + horizontal_alignment, vertical_alignment, textStyle, + verticalText, textDirection, rotation, imageLayout, + paddingLeft, paddingRight, spacingBefore, spacingAfter, lineSpacing); + } + + /** + * 设置单元格方向 + * + * @param textDirection 文本方向w + * @return 样式t + */ + public NewFormStyle deriveTextDirection(int textDirection) { + return getInstance(background, format, frFont, + border_top, border_top_color, border_bottom, border_bottom_color, + border_left, border_left_color, border_right, border_right_color, + horizontal_alignment, vertical_alignment, textStyle, + verticalText, textDirection, rotation, imageLayout, + paddingLeft, paddingRight, spacingBefore, spacingAfter, lineSpacing); + } + + /** + * 设置单元格垂直方向 + * + * @param verticalText 垂直方向c + * @return 样式 + */ + public NewFormStyle deriveVerticalText(int verticalText) { + return getInstance(background, format, frFont, + border_top, border_top_color, border_bottom, border_bottom_color, + border_left, border_left_color, border_right, border_right_color, + horizontal_alignment, vertical_alignment, textStyle, + verticalText, textDirection, rotation, imageLayout, + paddingLeft, paddingRight, spacingBefore, spacingAfter, lineSpacing); + } + + /** + * 设置旋转方向 + * + * @param rotation 旋转方向 the new rotation of cell (between -90 and 90 degrees). + * @return 样式 + */ + public NewFormStyle deriveRotation(int rotation) { + return getInstance(background, format, frFont, + border_top, border_top_color, border_bottom, border_bottom_color, + border_left, border_left_color, border_right, border_right_color, + horizontal_alignment, vertical_alignment, textStyle, + verticalText, textDirection, rotation, imageLayout, + paddingLeft, paddingRight, spacingBefore, spacingAfter, lineSpacing); + } + + /** + * 设置图片布局 + * + * @param imageLayout the new image layout 图片布局t + * @return 样式 + * @see Constants#IMAGE_TILED + * @see Constants#IMAGE_CENTER + * @see Constants#IMAGE_EXTEND + */ + public NewFormStyle deriveImageLayout(int imageLayout) { + return getInstance(background, format, frFont, + border_top, border_top_color, border_bottom, border_bottom_color, + border_left, border_left_color, border_right, border_right_color, + horizontal_alignment, vertical_alignment, textStyle, + verticalText, textDirection, rotation, imageLayout, + paddingLeft, paddingRight, spacingBefore, spacingAfter, lineSpacing); + } + + /** + * 设置缩进 + * + * @param paddingLeft 左缩进 + * @param paddingRight 右缩进 + * @return 样式 + */ + public NewFormStyle derivePadding(int paddingLeft, int paddingRight) { + return getInstance(background, format, frFont, + border_top, border_top_color, border_bottom, border_bottom_color, + border_left, border_left_color, border_right, border_right_color, + horizontal_alignment, vertical_alignment, textStyle, + verticalText, textDirection, rotation, imageLayout, + paddingLeft, paddingRight, spacingBefore, spacingAfter, lineSpacing); + } + + /** + * 设置左缩进 + * + * @param paddingLeft 左缩进 + * @return 样式 + */ + public NewFormStyle derivePaddingLeft(int paddingLeft) { + return getInstance(background, format, frFont, + border_top, border_top_color, border_bottom, border_bottom_color, + border_left, border_left_color, border_right, border_right_color, + horizontal_alignment, vertical_alignment, textStyle, + verticalText, textDirection, rotation, imageLayout, + paddingLeft, paddingRight, spacingBefore, spacingAfter, lineSpacing); + } + + /** + * 设置右缩进 + * + * @param paddingRight 右缩进 + * @return 样式 + */ + public NewFormStyle derivePaddingRight(int paddingRight) { + return getInstance(background, format, frFont, + border_top, border_top_color, border_bottom, border_bottom_color, + border_left, border_left_color, border_right, border_right_color, + horizontal_alignment, vertical_alignment, textStyle, + verticalText, textDirection, rotation, imageLayout, + paddingLeft, paddingRight, spacingBefore, spacingAfter, lineSpacing); + } + + /** + * 设置段前间距 + * + * @param spacingBefore 段前间距d + * @return 样式 y + */ + public NewFormStyle deriveSpacingBefore(int spacingBefore) { + return getInstance(background, format, frFont, + border_top, border_top_color, border_bottom, border_bottom_color, + border_left, border_left_color, border_right, border_right_color, + horizontal_alignment, vertical_alignment, textStyle, + verticalText, textDirection, rotation, imageLayout, + paddingLeft, paddingRight, spacingBefore, spacingAfter, lineSpacing); + } + + /** + * 设置段后间距 + * + * @param spacingAfter 段后间距d + * @return 样式y + */ + public NewFormStyle deriveSpacingAfter(int spacingAfter) { + return getInstance(background, format, frFont, + border_top, border_top_color, border_bottom, border_bottom_color, + border_left, border_left_color, border_right, border_right_color, + horizontal_alignment, vertical_alignment, textStyle, + verticalText, textDirection, rotation, imageLayout, + paddingLeft, paddingRight, spacingBefore, spacingAfter, lineSpacing); + } + + /** + * 设置行间距 + * + * @param lineSpacing 行间距h + * @return 样式y + */ + public NewFormStyle deriveLineSpacing(int lineSpacing) { + return getInstance(background, format, frFont, + border_top, border_top_color, border_bottom, border_bottom_color, + border_left, border_left_color, border_right, border_right_color, + horizontal_alignment, vertical_alignment, textStyle, + verticalText, textDirection, rotation, imageLayout, + paddingLeft, paddingRight, spacingBefore, spacingAfter, lineSpacing); + } + + /** + * 对象转为json格式 + * + * @param repo 浏览器上下文 + * @param size 单元格大小 + * @return json格式数据 + */ + public JSONObject toJSONObject(Repository repo, Dimension size) throws JSONException { + JSONObject jo = new JSONObject(); + if (background != null) { + jo.put("background", background.toJSONObject(repo, size)); + } + // 不是默认值才输出,节省流量 + if (frFont != null && !ComparatorUtils.equals(frFont, FRContext.getDefaultValues().getFRFont())) { + jo.put("font", frFont.toJSONObject(repo)); + } + + if (!checkLeftRightTopBottom4b0()) { + jo.put("border", border2JSONObject()); + jo.put("borderCss", borderClsCss); + } + + createAlignAndTextStyleConfig(jo); + if (rotation != 0) { + jo.put("ro", rotation); + } + if (imageLayout != Constants.IMAGE_CENTER) { + jo.put("il", imageLayout); + } + createSpacingAndPaddingConfig(jo); + + return jo; + } + + private void createAlignAndTextStyleConfig(JSONObject jo) throws JSONException { + if (horizontal_alignment != Constants.NULL) { + jo.put("ha", horizontal_alignment); + } + if (vertical_alignment != Constants.CENTER) { + jo.put("va", vertical_alignment); + } + if (textStyle != TEXTSTYLE_WRAPTEXT) { + jo.put("ts", textStyle); + } + if (verticalText != NewFormStyle.HORIZONTALTEXT) { + jo.put("vt", verticalText); + } + if (textDirection != RIGHT_TO_LEFT) { + jo.put("td", textDirection); + } + jo.put("contentCss", contentClsCss); + } + + private void createSpacingAndPaddingConfig(JSONObject jo) throws JSONException { + if (paddingLeft != DEFAULT_PADDING) { + jo.put("pl", paddingLeft); + } + if (paddingRight != DEFAULT_PADDING) { + jo.put("pr", paddingRight); + } + if (spacingBefore != 0) { + jo.put("sb", spacingBefore); + } + if (spacingAfter != 0) { + jo.put("sa", spacingAfter); + } + if (lineSpacing != 0) { + jo.put("ls", lineSpacing); + } + } + + /** + * 获取字体 + */ + public FRFont getFRFont() { + if (this.frFont == null) { + this.frFont = FRContext.getDefaultValues().getFRFont(); + } + return this.frFont; + } + + /** + * Gets cell format. + */ + public Format getFormat() { + return this.format; + } + + /** + * Gets background. + */ + public Background getBackground() { + return this.background; + } + + /** + * Gets the type of border to use for the top border of the cell. + * + * @see Constants#LINE_NONE + * @see Constants#LINE_THIN + * @see Constants#LINE_MEDIUM + * @see Constants#LINE_DASH + * @see Constants#LINE_HAIR + * @see Constants#LINE_HAIR2 + * @see Constants#LINE_THICK + * @see Constants#LINE_DOUBLE + * @see Constants#LINE_DOT + * @see Constants#LINE_MEDIUM_DASH + * @see Constants#LINE_DASH_DOT + * @see Constants#LINE_MEDIUM_DASH_DOT + * @see Constants#LINE_DASH_DOT_DOT + * @see Constants#LINE_MEDIUM_DASH_DOT_DOT + * @see Constants#LINE_SLANTED_DASH_DOT + */ + public int getBorderTop() { + return this.border_top; + } + + /** + * 获取上边框线的宽度 + */ + public int getBorderTopWidth() { + return GraphHelper.getLineStyleSize(this.getBorderTop()); + } + + /** + * Gets the type of border to use for the left border of the cell. + * + * @see Constants#LINE_NONE + * @see Constants#LINE_THIN + * @see Constants#LINE_MEDIUM + * @see Constants#LINE_DASH + * @see Constants#LINE_HAIR + * @see Constants#LINE_HAIR2 + * @see Constants#LINE_THICK + * @see Constants#LINE_DOUBLE + * @see Constants#LINE_DOT + * @see Constants#LINE_MEDIUM_DASH + * @see Constants#LINE_DASH_DOT + * @see Constants#LINE_MEDIUM_DASH_DOT + * @see Constants#LINE_DASH_DOT_DOT + * @see Constants#LINE_MEDIUM_DASH_DOT_DOT + * @see Constants#LINE_SLANTED_DASH_DOT + */ + public int getBorderLeft() { + return this.border_left; + } + + public int getBorderLeftWidth() { + return GraphHelper.getLineStyleSize(this.getBorderLeft()); + } + + public int getBorderRightWidth() { + return GraphHelper.getLineStyleSize(this.getBorderRight()); + } + + public int getBorderBottomWidth() { + return GraphHelper.getLineStyleSize(this.getBorderBottom()); + } + + /** + * Gets the type of border to use for the bottom border of the cell. + * + * @see Constants#LINE_NONE + * @see Constants#LINE_THIN + * @see Constants#LINE_MEDIUM + * @see Constants#LINE_DASH + * @see Constants#LINE_HAIR + * @see Constants#LINE_HAIR2 + * @see Constants#LINE_THICK + * @see Constants#LINE_DOUBLE + * @see Constants#LINE_DOT + * @see Constants#LINE_MEDIUM_DASH + * @see Constants#LINE_DASH_DOT + * @see Constants#LINE_MEDIUM_DASH_DOT + * @see Constants#LINE_DASH_DOT_DOT + * @see Constants#LINE_MEDIUM_DASH_DOT_DOT + * @see Constants#LINE_SLANTED_DASH_DOT + */ + public int getBorderBottom() { + return this.border_bottom; + } + + /** + * Gets the type of border to use for the right border of the cell. + * + * @see Constants#LINE_NONE + * @see Constants#LINE_THIN + * @see Constants#LINE_MEDIUM + * @see Constants#LINE_DASH + * @see Constants#LINE_HAIR + * @see Constants#LINE_HAIR2 + * @see Constants#LINE_THICK + * @see Constants#LINE_DOUBLE + * @see Constants#LINE_DOT + * @see Constants#LINE_MEDIUM_DASH + * @see Constants#LINE_DASH_DOT + * @see Constants#LINE_MEDIUM_DASH_DOT + * @see Constants#LINE_DASH_DOT_DOT + * @see Constants#LINE_MEDIUM_DASH_DOT_DOT + * @see Constants#LINE_SLANTED_DASH_DOT + */ + public int getBorderRight() { + return this.border_right; + } + + /** + * Gets the color of border to use for the top border of the cell. + */ + public Color getBorderTopColor() { + return this.border_top_color; + } + + /** + * Gets the color of border to use for the left border of the cell. + */ + public Color getBorderLeftColor() { + return this.border_left_color; + } + + /** + * Gets the color of border to use for the bottom border of the cell. + */ + public Color getBorderBottomColor() { + return this.border_bottom_color; + } + + /** + * Gets the color of border to use for the right border of the cell. + */ + public Color getBorderRightColor() { + return this.border_right_color; + } + + /** + * Gets the type of horizontal alignment for the cell + * + * @see BaseUtils#getAlignment4Horizontal(NewFormStyle style, Object value) + * @see BaseUtils#getAlignment4Horizontal(NewFormStyle style) + * @see Constants#LEFT + * @see Constants#CENTER + * @see Constants#RIGHT + * @deprecated suggest not directly use; + */ + public int getHorizontalAlignment() { + return this.horizontal_alignment; + } + + /** + * Gets the type of vertical alignment for the cell + * + * @see Constants#TOP + * @see Constants#CENTER + * @see Constants#BOTTOM + */ + public int getVerticalAlignment() { + return this.vertical_alignment; + } + + /** + * Gets the text style. + * + * @see NewFormStyle#TEXTSTYLE_WRAPTEXT + * @see NewFormStyle#TEXTSTYLE_SINGLELINE + * @see NewFormStyle#TEXTSTYLE_SINGLELINEADJUSTFONT + * @see NewFormStyle#TEXTSTYLE_MULTILINEADJUSTFONT + */ + public int getTextStyle() { + return textStyle; + } + + /** + * Gets the value of verticalText + */ + public int getVerticalText() { + return this.verticalText; + } + + /** + * Gets the value of textDirection + */ + public int getTextDirection() { + return this.textDirection; + } + + /** + * Gets the degree of rotation for the text in the cell + * (between -90 and 90 degrees). + */ + public int getRotation() { + return this.rotation; + } + + /** + * Gets the image layout + * + * @return the image layout + * @see Constants#IMAGE_TILED + * @see Constants#IMAGE_CENTER + * @see Constants#IMAGE_EXTEND + */ + public int getImageLayout() { + return imageLayout; + } + + /** + * get indentation value + */ + public int getPaddingLeft() { + return this.paddingLeft; + } + + /** + * get indentation value + */ + public int getPaddingRight() { + return this.paddingRight; + } + + /** + * @return the spacingBefore + */ + public byte getSpacingBefore() { + return spacingBefore; + } + + /** + * @return the spacingAfter + */ + public byte getSpacingAfter() { + return spacingAfter; + } + + /** + * @return the lineSpacing + */ + public byte getLineSpacing() { + return lineSpacing; + } + + + /** + * 如果单元格内容不为数字或者字符串的时候使用静态数据 + * + * @return + */ + public String getContentClsCss() { + return contentClsCss; + } + + /** + * 若value为数字或字符串需要判断一下是否可以转换为数字显示 + * + * @param value + * @return + */ + public String getContentClsCss(Object value) { + return this.contentStyle2class(this.contentStyleCssMap, value); + } + + /** + * getContentStyleCssMap + */ + public Map getContentStyleCssMap() { + return contentStyleCssMap; + } + + /** + * getBorderClsCss + */ + public String getBorderClsCss() { + return borderClsCss; + } + + /** + * getBorderStyleCssMap + */ + public Map getBorderStyleCssMap() { + return borderStyleCssMap; + } + + /** + * contentStyle2class + */ + private String contentStyle2class(Map contentCssMap) { + return contentStyle2class(contentCssMap, null); + } + + protected String contentStyle2class(Map contentCssMap, Object value) { + StringBuilder sb = new StringBuilder(); + sb.append("fh"); + int horizontal_alignment = BaseUtils.getAlignment4Horizontal(this, value); + switch (horizontal_alignment) { + case Constants.CENTER: + sb.append(" tac"); + break; + case Constants.RIGHT: + sb.append(" tar"); + break; + } + switch (this.vertical_alignment) { + case Constants.BOTTOM: + sb.append(" vab"); + break; + case Constants.TOP: + sb.append(" vat"); + break; + } + + if (this.textStyle != NewFormStyle.TEXTSTYLE_WRAPTEXT + && this.textStyle != NewFormStyle.TEXTSTYLE_MULTILINEADJUSTFONT) { + sb.append(" nw"); + } else { + sb.append(" bw"); // alex:只可以用于ie,英文的长单词会被换行分开 + } + + FRFont font = this.getFRFont(); + if (font != null) { + sb.append(font2Class(font, contentCssMap, lineSpacing)); + } + + int lit = this.paddingLeft; + int rit = this.paddingRight; + int tit = this.spacingBefore; + int bit = this.spacingAfter; + //这边的<= 包括下面<>修改的话, 都要做好兼容的 + if (lit <= DEFAULT_PADDING) { + sb.append(" pl" + lit); + } else { + contentCssMap.put("padding-left", lit + "pt"); + } + + if (rit < DEFAULT_PADDING) { + sb.append(" pr" + rit); + } else if (rit > DEFAULT_PADDING) { + contentCssMap.put("padding-right", rit + "pt"); + } + + if (0 < tit && tit <= DEFAULT_PADDING) { + sb.append(" pt" + tit); + } else if (tit > DEFAULT_PADDING) { + contentCssMap.put("padding-top", tit + "pt"); + } + + if (0 < bit && bit <= DEFAULT_PADDING) { + sb.append(" pb" + bit); + } else if (bit > DEFAULT_PADDING) { + contentCssMap.put("padding-bottom", bit + "pt"); + } + + return sb.toString().trim(); + } + + public JSONObject border2JSONObject() throws JSONException { + JSONObject borderJSON = new JSONObject(); + if (checkLeftRightTopBottom4b1()) { + borderJSON.put("b1", true); + } else { + border2JSONObject(borderJSON, border_top, border_top_color, "t"); + border2JSONObject(borderJSON, border_left, border_left_color, "l"); + border2JSONObject(borderJSON, border_bottom, border_bottom_color, "b"); + border2JSONObject(borderJSON, border_right, border_right_color, "r"); + } + return borderJSON; + } + + private void border2JSONObject(JSONObject jo, byte borderStyle, Color borderColor, String position) throws JSONException { + if (borderStyle == Constants.LINE_THIN + && ComparatorUtils.equals(borderColor, Color.black)) { + jo.put(position, new JSONObject().put("w", 1)); + } else { + int bw = BaseUtils.getBorderWidth(borderStyle); + if (bw > 0) { + JSONObject extra = new JSONObject(); + extra.put("w", bw); + if (!ComparatorUtils.equals(borderColor, Color.black)) { + extra.put("c", StableUtils.javaColorToCSSColor(borderColor)); + } + // 留着保持兼容老的app + extra.put("s", borderLine2String(borderStyle)); + // MOBILE-21527 报表设计器设置不同的border后台返回具体的border类型 + extra.put("type", borderStyle); + jo.put(position, extra); + } + } + } + + private String borderLine2String(int borderStyle) { + switch (borderStyle) { + case Constants.LINE_THIN: + case Constants.LINE_SLIM: + return "t"; + case Constants.LINE_MEDIUM: + return "m"; + case Constants.LINE_THICK: + return "tk"; + case Constants.LINE_DOUBLE: + return "de"; + case Constants.LINE_DOT: + return "dt"; + case Constants.LINE_DASH_DOT: + return "dadt"; + case Constants.LINE_DASH_DOT_DOT: + return "ddd"; + default: + return "dash"; + } + } + + /* + * 返回classBuf与styleBuf + */ + private String border2Class(Map borderCssMap) { + StringBuilder sb = new StringBuilder(); + // 简化class + if (checkLeftRightTopBottom4b0()) { + sb.append(" b0"); + return sb.toString().trim(); + } else if (checkLeftRightTopBottom4b1()) { + sb.append(" b1"); + return sb.toString().trim(); + } + + // right + visible_border_css(this.border_right, this.border_right_color, sb, borderCssMap, "r"); + // bottom + visible_border_css(this.border_bottom, this.border_bottom_color, sb, borderCssMap, "b"); + // left + visible_border_css(this.border_left, this.border_left_color, sb, borderCssMap, "l"); + // top + visible_border_css(this.border_top, this.border_top_color, sb, borderCssMap, "t"); + + return sb.toString().trim(); + } + + private boolean checkLeftRightTopBottom4b0() { + return this.border_left == Constants.LINE_NONE && this.border_right == Constants.LINE_NONE + && this.border_top == Constants.LINE_NONE && this.border_bottom == Constants.LINE_NONE; + } + + private boolean checkLeftRightTopBottom4b1() { + return this.border_left == Constants.LINE_THIN && this.border_right == Constants.LINE_THIN + && this.border_top == Constants.LINE_THIN && this.border_bottom == Constants.LINE_THIN + && ComparatorUtils.equals(this.border_left_color, Color.black) + && ComparatorUtils.equals(this.border_right_color, Color.black) + && ComparatorUtils.equals(this.border_top_color, Color.black) + && ComparatorUtils.equals(this.border_bottom_color, Color.black); + } + + private static void visible_border_css( + byte border_style, Color border_color, StringBuilder classBuf, Map borderCssMap, String border_position + ) {// right + if (border_style == Constants.LINE_THIN + && ComparatorUtils.equals(border_color, Color.black)) { + classBuf.append(" b" + border_position + "1"); + } + + int bw = BaseUtils.getBorderWidth(border_style); + if (bw > 0) { + // width + classBuf.append(" b" + border_position + "w" + bw); + + // style + classBuf.append(" " + __border2Class(border_style, border_position)); + + // color + // 细线需要另外处理为 rgba,rgba的边框在chrome上会出现渲染问题 + // 转为 rgb 显示 + if (border_style == Constants.LINE_SLIM) { + borderCssMap.put(_position2Colorcss(border_position), + StableUtils.javaColorToCSSColor(brighter(border_color))); + } else if (ComparatorUtils.equals(border_color, Color.black)) { + classBuf.append(" b" + border_position + "cb"); + } else { + borderCssMap.put(_position2Colorcss(border_position), StableUtils.javaColorToCSSColor(border_color)); + } + } else { + classBuf.append(" b" + border_position + "0"); + } + } + + private static Color brighter(Color ori) { + return new Color(Math.min((int) ((1 - FACTORY_ALPHA) * 255 + FACTORY_ALPHA * ori.getRed()), 255), + Math.min((int) ((1 - FACTORY_ALPHA) * 255 + FACTORY_ALPHA * ori.getGreen()), 255), + Math.min((int) ((1 - FACTORY_ALPHA) * 255 + FACTORY_ALPHA * ori.getBlue()), 255), + 255); + } + + private static String _position2Colorcss(String border_position) { + if (ComparatorUtils.equals(border_position, "r")) { + return "border-right-color"; + } else if (ComparatorUtils.equals(border_position, "b")) { + return "border-bottom-color"; + } else if (ComparatorUtils.equals(border_position, "l")) { + return "border-left-color"; + } else if (ComparatorUtils.equals(border_position, "t")) { + return "border-top-color"; + } else { + return "border-color"; + } + } + + private static String __border2Class(int borderStyle, String d) { + switch (borderStyle) { + case Constants.LINE_SLIM: + return "b" + d + "ss"; + case Constants.LINE_THIN: + return "b" + d + "ss"; + case Constants.LINE_MEDIUM: + return "b" + d + "ss"; + case Constants.LINE_THICK: + return "b" + d + "ss"; + case Constants.LINE_DOUBLE: + return "b" + d + "sd"; + case Constants.LINE_DOT: + return "b" + d + "sdo"; + case Constants.LINE_DASH_DOT: + return "b" + d + "sdo"; + case Constants.LINE_DASH_DOT_DOT: + return "b" + d + "sdo"; + default: + return "b" + d + "sda"; + } + } + + private static String font2Class(Font font, Map cssMap, byte lineSpacing) { + StringBuilder sb = new StringBuilder(); + if (font.isBold()) { + sb.append(" fwb"); + } + if (font.isItalic()) { + sb.append(" fsi"); + } + String fontName = font.getName(); + String cls = (String) getclsFontFamily().get(fontName); + if (cls == null) { + // REPORT-25741 部分字体需要加引号才能识别 + cssMap.put("font-family", "'" + fontName + "'"); + } else if (!ComparatorUtils.equals(cls, "fnss")) { // alex:font-family:SimSun写在fh里面了 + sb.append(" " + cls); + } + float size = font.getSize2D(); + if (AssistUtils.equals(size, N_SIZE)) { // alex:font-size:9pt写在fh里面 + } else if (size >= MIN_SIZE && size <= MAX_SIZE) { + size = Utils.round5(size); + sb.append(" f" + String.valueOf(size).replace('.', '-')); + } else { + cssMap.put("font-size", font.getSize2D() + "pt"); + } + + if (lineSpacing != 0) { + // carl:直接在这里添上line-height + //cellFM.getHeight() 不能简单的 *(4/3)进行转换,数值不对 + FontMetrics cellFM = GraphHelper.getFontMetrics(FRFont.getInstance(font).applyResolutionNP(Constants.FR_PAINT_RESOLUTION)); + cssMap.put("line-height", (int) (cellFM.getHeight() + lineSpacing * LINE_HEIGHT_FIX / LINE_HEIGHT_FIX2) + "px"); + } + + font2ClassOnFRFont(font, cssMap, sb); + + return sb.toString(); + } + + private static void font2ClassOnFRFont(Font font, Map cssMap, StringBuilder sb) { + if (font instanceof FRFont) { + if (((FRFont) font).isStrikethrough()) { + if (((FRFont) font).getUnderline() != Constants.LINE_NONE) { + sb.append(" tdstu"); + } else { + sb.append(" tdst"); + } + } else { + if (((FRFont) font).getUnderline() != Constants.LINE_NONE) { + sb.append(" tdu"); + } + } + if (((FRFont) font).isSubscript()) { + sb.append(" suber"); + } else if (((FRFont) font).isSuperscript()) { + sb.append(" super"); + } + if (((FRFont) font).isShadow()) { + sb.append(" shadow"); + } + Color color = ((FRFont) font).getForeground(); + if (!ComparatorUtils.equals(color, Color.black) && color != null) { + cssMap.put("color", StableUtils.javaColorToCSSColor(color)); + } + } + } + + + private transient int hashCode = -1; + + /** + * 哈希码 + * + * @return 哈希值h + */ + public int hashCode() { + + if (hashCode == -1) { + //因为format和非ColorBackground的Background并不是唯一的(也就是说即使equals但hashCode也会不一样),所以不放在hashCode的计算里面 + int code = this.border_top ^ this.border_bottom ^ this.border_left ^ this.border_right ^ + this.border_top_color.hashCode() ^ this.border_bottom_color.hashCode() ^ + this.border_left_color.hashCode() ^ this.border_right_color.hashCode() ^ + this.horizontal_alignment ^ this.imageLayout ^ this.verticalText ^ + this.paddingLeft ^ this.paddingRight ^ this.rotation ^ this.textDirection ^ this.textStyle ^ this.vertical_alignment; + + if (frFont != null) { + code ^= frFont.hashCode(); + } + if (background != null) { + code = background.fixHashCode(code); + } + hashCode = code; + } + return hashCode; + } + + /** + * Equals. + */ + public boolean equals(Object object) { + if (object == this) { + return true; + } + if (!(object instanceof NewFormStyle)) { + return false; + } + + NewFormStyle newStyle = (NewFormStyle) object; + + //由于java中所有的Format都实现了equals()方法,这里可以直接来比较. + return ComparatorUtils.equals(newStyle.getFormat(), this.getFormat()) && + ComparatorUtils.equals(newStyle.getFRFont(), this.getFRFont()) && + ComparatorUtils.equals(newStyle.getBackground(), this.getBackground()) && + + newStyle.getBorderTop() == this.getBorderTop() && + newStyle.getBorderLeft() == this.getBorderLeft() && + newStyle.getBorderBottom() == this.getBorderBottom() && + newStyle.getBorderRight() == this.getBorderRight() && + + ComparatorUtils.equals(newStyle.getBorderTopColor(), this.getBorderTopColor()) && + ComparatorUtils.equals(newStyle.getBorderLeftColor(), this.getBorderLeftColor()) && + ComparatorUtils.equals(newStyle.getBorderBottomColor(), this.getBorderBottomColor()) && + ComparatorUtils.equals(newStyle.getBorderRightColor(), this.getBorderRightColor()) && + + BaseUtils.getAlignment4Horizontal(newStyle) == BaseUtils.getAlignment4Horizontal(this) && + newStyle.getVerticalAlignment() == this.getVerticalAlignment() && + newStyle.getTextStyle() == this.getTextStyle() && + + //james verticalText + newStyle.getVerticalText() == this.getVerticalText() && + newStyle.getTextDirection() == this.getTextDirection() && + newStyle.getRotation() == this.getRotation() && + newStyle.getImageLayout() == this.getImageLayout() && + + // alex padding + newStyle.getPaddingLeft() == this.getPaddingLeft() && + newStyle.getPaddingRight() == this.getPaddingRight() && + + //间距 + newStyle.getSpacingBefore() == this.getSpacingBefore() && + newStyle.getSpacingAfter() == this.getSpacingAfter() && + newStyle.getLineSpacing() == this.getLineSpacing(); + } + + /** + * Clone. + */ + public Object clone() throws CloneNotSupportedException { + return this; + } + + protected Object readResolve() throws InvalidObjectException { + if (this.getClass() != NewFormStyle.class) { + throw new InvalidObjectException("subclass didn't correctly implement readResolve"); + } + + Object res = initializeStyle.get(this); + //alex:从ObjectInputStream生成的Object可能不存在于全局的map中,可能为null + if (res == null) { + res = this; + initializeStyle.put(this, res); + } + + return res; + } + + /** + * 画单元格的Value + *

+ * 通过width, height方式画 + */ + public static void paintContent(Graphics2D g2d, Object value, NewFormStyle style, int width, int height, int resolution) { + // kunsnat: 添加判断width和height. 是否有必要继续paint, @bug0006854, 增加了混乱的字符. + if (value == null || width == 0 || height == 0) { + return; + } + + if (style == null) { + //peter:获取默认的Style. + style = NewFormStyle.DEFAULT_STYLE.deriveImageLayout(Constants.IMAGE_CENTER); + } + + if (value instanceof BaseFormula) { + value = ((BaseFormula) value).getResult(); + } + + if (value instanceof Painter) { + // TODO 弹出界面然后 esc bug.. + ((Painter) value).paint(g2d, width, height, resolution, style); + } else if (value instanceof ImageProvider) { + paintImageContent(g2d, ((ImageProvider) value).getImage(), style, width, height, resolution); + } else if (value instanceof Image) { + paintImageContent(g2d, (Image) value, style, width, height, resolution); + } else { + //默认Paint String + String text = valueToText(value, style.getFormat()); + paintCellStyleString(g2d, width, height, text, style, resolution); + } + } + + public static void paintImageContent(Graphics2D g2d, Image value, NewFormStyle style, int width, int height, int resolution) { + int imageLayout = CoreGraphHelper.changeImageLayout4Draw(value, style.getImageLayout(), width, height); + + //这边做scale的目的是为了在导出图片以及以图片方式打印时,根据不同的resolution调整对应的dpi + int defaultResolution = Constants.DEFAULT_WEBWRITE_AND_SCREEN_RESOLUTION; + double scale = resolution * 1d / defaultResolution; + width = (int) (width / scale); + height = (int) (height / scale); + g2d.scale(scale, scale); + + GraphHelper.paintImage(g2d, width, height, value, imageLayout, + style.getHorizontalAlignment(), style.getVerticalAlignment(), -1, -1); + //最后把画板改回来 + g2d.scale(defaultResolution * 1d / resolution, defaultResolution * 1d / resolution); + } + + /** + * 画单元格的Value + *

+ * 通过PaintRectangle + ClipRectangle画 + */ + public static void paintContent(Graphics2D g2d, Object value, int resolution, NewFormStyle style, Rectangle paintRectangle, Rectangle clipRectangle) { + Shape oldClip = g2d.getClip(); + g2d.translate(paintRectangle.getX(), paintRectangle.getY()); + g2d.clip(clipRectangle); // kunsnat: 这些clip 都没有考虑边框啊!!! 所有图表的边框压缩到里面去了. + + paintContent(g2d, value, style, paintRectangle.width, paintRectangle.height, resolution); + + g2d.translate(-paintRectangle.getX(), -paintRectangle.getY()); + g2d.setClip(oldClip); + } + + /** + * 画背景 + *

+ * 通过width, height方式画 + */ + public static void paintBackground(Graphics2D g2d, NewFormStyle style, double width, double height) { + if (style == null) {// 还是需要检查一下的,假如DefaultCellStyle拥有Border就需要Paint了. + return; + } + + // paint background. + Background background = style.getBackground(); + if (background == null) { + return; + } + + background.layoutDidChange((int) width, (int) height); + synchronized (tempRectangle2D) { + tempRectangle2D.setRect(0, 0, width, height); + background.paint(g2d, tempRectangle2D); + } + } + + /** + * 画单元格的背景 + *

+ * 通过PaintRectangle + ClipRectangle画 + */ + public static void paintBackground(Graphics2D g2d, NewFormStyle style, Rectangle paintRectangle, Rectangle clipRectangle) { + if (style == null) { + //peter:获取默认的Style. + style = NewFormStyle.DEFAULT_STYLE; + } + Shape oldClip = g2d.getClip(); + g2d.translate(paintRectangle.getX(), paintRectangle.getY()); + g2d.clip(clipRectangle); + Background background = style.getBackground(); + + if (background != null) { + // alex:根据paintRectangle和clipRectangle共同决定Background的PaintRectangle + background.paint(g2d, new Rectangle( + clipRectangle.x, clipRectangle.y, + clipRectangle.width, clipRectangle.height)); + } + g2d.translate(-paintRectangle.getX(), -paintRectangle.getY()); + g2d.setClip(oldClip); + } + + /** + * 画边框 + *

+ * 通过width, height方式画 + */ + public static void paintBorder(Graphics2D g2d, NewFormStyle style, double width, double height) { + if (style == null) {// 还是需要检查一下的,假如DefaultCellStyle拥有Border就需要Paint了. + return; + } + + //peter:当是粗线的时候,需要计算尺寸 + double left = 0; + double right = 0; + if (style.getBorderLeft() != Constants.LINE_NONE) { + int borderLeftSize = GraphHelper.getLineStyleSize(style.getBorderLeft()); + left = borderLeftSize / 2; + } + if (style.getBorderRight() != Constants.LINE_NONE) { + int borderRightSize = GraphHelper.getLineStyleSize(style.getBorderRight()); + right = borderRightSize / 2; + } + + double top = 0; + double bottom = 0; + if (style.getBorderTop() != Constants.LINE_NONE) { + int bSize = GraphHelper.getLineStyleSize(style.getBorderTop()); + top = bSize / 2; + } + if (style.getBorderBottom() != Constants.LINE_NONE) { + int bSize = GraphHelper.getLineStyleSize(style.getBorderBottom()); + bottom = bSize / 2; + } + + // paint border + if (style.getBorderTop() != Constants.LINE_NONE) { + g2d.setPaint(style.getBorderTopColor()); + GraphHelper.drawLine(g2d, 0 - left, 0, width + right, 0, style.getBorderTop()); + } + if (style.getBorderLeft() != Constants.LINE_NONE) { + g2d.setPaint(style.getBorderLeftColor()); + GraphHelper.drawLine(g2d, 0, 0 - top, 0, height + bottom, style.getBorderLeft()); + } + if (style.getBorderBottom() != Constants.LINE_NONE) { + g2d.setPaint(style.getBorderBottomColor()); + GraphHelper.drawLine(g2d, 0 - left, height, width + right, height, style.getBorderBottom()); + } + if (style.getBorderRight() != Constants.LINE_NONE) { + g2d.setPaint(style.getBorderRightColor()); + GraphHelper.drawLine(g2d, width, 0 - top, width, height + bottom, style.getBorderRight()); + } + } + + /** + * 画单元格的边框 + *

+ * 通过PaintRectangle + ClipRectangle画边框 + */ + public static void paintBorder(Graphics2D g2d, NewFormStyle style, Rectangle paintRectangle, Rectangle clipRectangle) { + //单元格行高列宽=0时, 不需要绘制边框 + if (paintRectangle.width == 0 || paintRectangle.height == 0) { + return; + } + g2d.translate(paintRectangle.getX(), paintRectangle.getY()); + + Rectangle realPaintRect = new Rectangle(0, 0, + paintRectangle.width, paintRectangle.height); + //peter:需要判断的因为clipRectangle极可能不是null. + if (clipRectangle != null) { + realPaintRect = realPaintRect.intersection(clipRectangle); + } + g2d.translate(realPaintRect.getX(), realPaintRect.getY()); + + if (style == null) {// 还是需要检查一下的,假如DefaultCellStyle拥有Border就需要Paint了. + //peter:获取默认的Style. + style = NewFormStyle.DEFAULT_STYLE; + } + + paintBorder(g2d, style, realPaintRect.getWidth(), realPaintRect.getHeight()); + + // 返回Translate + g2d.translate(-realPaintRect.getX(), -realPaintRect.getY()); + g2d.translate(-paintRectangle.getX(), -paintRectangle.getY()); + } + + /** + * 转化成文本 + * + * @param value 带转化的对象 + * @param format 格式g + * @return 字符串z + */ + public static String valueToText(Object value, Format format) { + //需要画的值,主要是把公式里面的值拿出来 + Object valueToPaint = value; + String text = null; + + String checkInfinity = StableUtils.checkInfinity(value); + if (StringUtils.isNotEmpty(checkInfinity)) { + return checkInfinity; + } + + if (value != null) { + if (value instanceof BaseFormula) { + BaseFormula formula = (BaseFormula) value; + if (StableUtils.isNull(formula.getResult())) { + return StringUtils.EMPTY;// BUG4788,应该显示空字符串 + } else { + valueToPaint = formula.getResult(); + } + } else if (value instanceof Clob) { + valueToPaint = DataBaseUtils.clob2String((Clob) value); + } + + if (format != null) { + format = FormatRepository.getInstance().getLocalFormat(format); + if (format instanceof DateFormat) {// Date. + text = valueInDateFormat((DateFormat) format, valueToPaint); + } else if (format instanceof NumberFormat) {// Number. + text = valueInNumberFormat(format, valueToPaint); + } else { + try { + text = format.format(valueToPaint); + } catch (Exception exp) { + text = Utils.objectToString(valueToPaint); + } + } + } else + text = Utils.objectToString(valueToPaint); + } else { + text = StringUtils.EMPTY; + } + + return text; + } + + protected static String valueInNumberFormat(Format format, Object valueToPaint) { + Number number = GeneralUtils.objectToNumberReturnNull(valueToPaint); + if (number != null) { + try { + if (format instanceof CoreDecimalFormat) { + //数字类型的要roundup一下, 默认会逢5奇进偶不进, 62.325->62.33 + return ((CoreDecimalFormat) format).formatRoundingModeUp(number); + } + //62.325->62.32 + return format.format(number); + } catch (Exception ignore) { + } + } + return Utils.objectToString(valueToPaint); + } + + private static String valueInDateFormat(DateFormat format, Object valueToPaint) { + + String result; + Date date2Format = tryGet(format, valueToPaint); + if (date2Format == null) { + date2Format = DateUtils.object2Date(valueToPaint, true); + } + if (date2Format != null) { + result = format.format(date2Format); + } + /* + * alex:如果date2Format为null,就是说valueToPaint不能转换成日期型,那么就直接画valueToPaint的字符串 + * 为什么必须要画valueToPaint的字符串呢?见bug0004566 + * 因为在Grid里面直接写=today(),并设置format为日期,如果不画,就一片空白,什么都看不到了 + */ + else { + result = Utils.objectToString(valueToPaint); + } + + return result; + } + + @Nullable + private static Date tryGet(DateFormat format, Object valueToPaint) { + + Date date2Format = null; + // 先用format来生成日期啊 + if (valueToPaint instanceof Date) { + date2Format = (Date) valueToPaint; + } else { + try { + date2Format = format.parse(GeneralUtils.objectToString(valueToPaint)); + } catch (Exception e) { + // nothing + } + } + return date2Format; + } + + /** + * Paint string according to Style. + */ + public static void paintCellStyleString2(Graphics2D g2d, int width, int height, String text, Style style, int resolution) { + if (StringUtils.isBlank(text)) { + return; + } + Paint oldPaint = g2d.getPaint(); + if (style == null) { + style = NewFormStyle.DEFAULT_STYLE; + } + Font rfont = BaseUtils.getStyleFont(g2d, style, resolution); + if (style.getRotation() != 0 && style.getVerticalText() == NewFormStyle.HORIZONTALTEXT) { + BaseUtils.drawStringStyleInRotation(g2d, width, height, text, style, resolution); + } else { + if (style.getTextStyle() == NewFormStyle.TEXTSTYLE_SINGLELINEADJUSTFONT + || style.getTextStyle() == NewFormStyle.TEXTSTYLE_MULTILINEADJUSTFONT) { + List lineTextList = BaseUtils.getLineTextList(text, style, rfont, height, width, resolution); + if (width <= 0 || lineTextList.isEmpty()) { + return; + } + int textAscent = g2d.getFontMetrics().getAscent(); + int textHeight = g2d.getFontMetrics().getHeight(); + + int textY = 0; + int textAllHeight = textHeight * lineTextList.size(); + double spacingBefore = PT.pt2pix(style.getSpacingBefore(), resolution); + double spacingAfter = PT.pt2pix(style.getSpacingAfter(), resolution); + double lineSpacing = PT.pt2pix(style.getLineSpacing(), resolution); + //james:加上间距,WORD中的行间距是从第一行文本的上面一行就算的,所以行间距形成的空隙与文本行数相同 + textAllHeight += spacingBefore + spacingAfter + lineSpacing * lineTextList.size(); + if (style.getVerticalAlignment() == Constants.CENTER) { + if (height > textAllHeight) {// 如果所有文本的高度小于当前可以绘区域的高度,就从0开始画字符. + textY = (height - textAllHeight) / 2; + } + } else if (style.getVerticalAlignment() == Constants.BOTTOM) { + textY = height - textAllHeight; + } + textY += textAscent;// 在绘画的时候,必须添加Ascent的高度. + textY += spacingBefore + lineSpacing;//james:加上"段前间距"+“行间距” + if (style.getTextStyle() == NewFormStyle.TEXTSTYLE_SINGLELINEADJUSTFONT) {// 通过自动调整字体显示所有的信息调整Font来显示所有的字符 + ContentLineGroup contentLineGroup = singleLineAdjustFont(style, lineTextList, width, height, g2d, resolution, true); + _drawContentLineGroup(contentLineGroup, g2d, textY, height, width, BaseUtils.getAlignment4Horizontal(style, text)); + } else if (style.getTextStyle() == NewFormStyle.TEXTSTYLE_MULTILINEADJUSTFONT) { + ContentLineGroup contentLineGroup = mulitiLineAdjustFont(style, lineTextList, width, height, g2d, resolution, true); + _drawContentLineGroup(contentLineGroup, g2d, textY, height, width, BaseUtils.getAlignment4Horizontal(style, text)); + } + } else { + BaseUtils.drawStringStyleInRotation(g2d, width, height, text, style, resolution); + } + } + g2d.setPaint(oldPaint); + } + + + + private static void _drawContentLineGroup( + ContentLineGroup contentLineGroup, Graphics2D g2d, + int textY, int height, int width, int horizontalAlignment + ) { + Style style = contentLineGroup.style; + + Font oldFRFont = g2d.getFont(); + g2d.setFont(style.getFRFont()); + if (style.getVerticalAlignment() == Constants.TOP) { + textY = 0; + } else if (style.getVerticalAlignment() == Constants.CENTER) { + if (height > contentLineGroup.totalHeight()) {// 如果所有文本的高度小于当前可以绘区域的高度,就从0开始画字符. + textY = (height - contentLineGroup.totalHeight()) / 2; + } else { + textY = 0; + } + } else if (style.getVerticalAlignment() == Constants.BOTTOM) { + textY = height - contentLineGroup.totalHeight(); + } + textY += g2d.getFontMetrics().getAscent();// 在绘画的时候,必须添加Ascent的高度. + + for (int i = 0; i < contentLineGroup.newLineTextList.size(); i++) { + double textX = style.getPaddingLeft(); + int textWidth = GraphHelper.getFontMetrics(style.getFRFont()).stringWidth((String) contentLineGroup.newLineTextList.get(i)); + if (horizontalAlignment == Constants.CENTER) { + textX = (width - textWidth - style.getPaddingRight()) / 2.0; + } else if (horizontalAlignment == Constants.RIGHT) { + textX = width - style.getPaddingRight() - textWidth; + } else if (horizontalAlignment == Constants.DISTRIBUTED) {// added by KevinWang + } + + if (horizontalAlignment == Constants.DISTRIBUTED) { + String lineText = (String) contentLineGroup.newLineTextList.get(i);// 分行显示时第i行的内容,但是也可能单行 + double charSpace = (width - style.getPaddingLeft() - style.getPaddingRight() + - g2d.getFontMetrics().charWidth(lineText.charAt(lineText.length() - 1))) + / (lineText.length() - 1); + for (int charIndex = 0; charIndex < lineText.length(); charIndex++) { + char tmpChar = lineText.charAt(charIndex);//取出一个字符待显示 + String tmpString = new String((new StringBuffer()).append(tmpChar));// 将其包装成string形式 + double charX = style.getPaddingLeft() + charIndex * charSpace; + GraphHelper.drawString(g2d, tmpString, charX, textY);//显示 + } + } else { + GraphHelper.drawString(g2d, (String) contentLineGroup.newLineTextList.get(i), textX, textY); + } + textY += contentLineGroup.newTextHeight; + } + g2d.setFont(oldFRFont); + } + + /* + * 通过自动调整字体显示所有的信息调整Font来显示所有的字符 + */ + private static ContentLineGroup singleLineAdjustFont(Style style, List lineTextList, int width, int height, Graphics2D g2d, int resolution, boolean fontScale) { + FRFont newFRFont = (FRFont) style.getFRFont().applyResolutionNP(resolution); + + int maxTextWidth = 0; + FontMetrics oriFontMetrics = GraphHelper.getFontMetrics(newFRFont); + int maxTextHeight = oriFontMetrics.getHeight() * lineTextList.size(); + String examle_text = ""; + for (int i = 0; i < lineTextList.size(); i++) { + if (oriFontMetrics.stringWidth((String) lineTextList.get(i)) > maxTextWidth) { + examle_text = (String) lineTextList.get(i); + maxTextWidth = oriFontMetrics.stringWidth((String) lineTextList.get(i)); + } + } + + if (maxTextWidth > 0 || maxTextHeight > 0) { // 只有当大于0时才改变字体大小 + // 调整宽度和高度 + if (maxTextWidth > (width - style.getPaddingLeft() - style.getPaddingRight()) || maxTextHeight > height) { + while (newFRFont.getSize() > 1) { + newFRFont = newFRFont.applySize((float) newFRFont.getSize() - 1); + maxTextWidth = GraphHelper.getFontMetrics(newFRFont).stringWidth(examle_text); + maxTextHeight = GraphHelper.getFontMetrics(newFRFont).getHeight() * lineTextList.size(); + + // 由于Font的变化单位是1, 所以必须这么找最适合的 Font + if (maxTextWidth <= (width - style.getPaddingLeft() - style.getPaddingRight()) && maxTextHeight <= height) { + break; + } + } + } + } + + FRFont returnFont = style.getFRFont().applySize(fontScale ? newFRFont.getSize2D() : FRFont.convResolution(newFRFont.getSize2D(), resolution)); + return new ContentLineGroup(style.deriveFRFont(returnFont), lineTextList, GraphHelper.getFontMetrics(newFRFont, g2d).getHeight()); + } + + /* + * 多行显示(调整字体大小),即固定单元格高和框,改变字体大小使适应。 + */ + private static ContentLineGroup mulitiLineAdjustFont(Style style, List lineTextList, int width, int height, Graphics2D g2d, int resolution, boolean fontScale) { + FRFont newFRFont = (FRFont) style.getFRFont().applyResolutionNP(resolution); + List newLineTextList; + NewFormStyle tmpStyle = NewFormStyle.DEFAULT_STYLE; + tmpStyle = tmpStyle.deriveTextStyle(NewFormStyle.TEXTSTYLE_WRAPTEXT); + int newTextHeight = GraphHelper.getFontMetrics(newFRFont, g2d).getHeight(); + + newLineTextList = resovleLineTextListByStyle(newFRFont, lineTextList, tmpStyle, width, resolution); + + if (newLineTextList.size() * newTextHeight > height) { + while (newFRFont.getSize() > 1) { + newFRFont = newFRFont.applySize((float) newFRFont.getSize() - 1); + newLineTextList = resovleLineTextListByStyle(newFRFont, lineTextList, tmpStyle, width, resolution); + + newTextHeight = GraphHelper.getFontMetrics(newFRFont, g2d).getHeight(); + if (newLineTextList.size() * newTextHeight <= height) { + break; + } + } + } else if (newLineTextList.size() * newTextHeight < height) { + while (newFRFont.getSize() < MAX_FONT_SIZE) { // _denny: 设定字体最大为100 + newFRFont = newFRFont.applySize((float) newFRFont.getSize() + 1); + + newLineTextList = resovleLineTextListByStyle(newFRFont, lineTextList, tmpStyle, width, resolution); + + newTextHeight = GraphHelper.getFontMetrics(newFRFont, g2d).getHeight(); + if (newLineTextList.size() * newTextHeight > height) { + newFRFont = newFRFont.applySize(newFRFont.getSize() - 1); + + newLineTextList = resovleLineTextListByStyle(newFRFont, lineTextList, tmpStyle, width, resolution); + + newTextHeight = GraphHelper.getFontMetrics(newFRFont, g2d).getHeight(); + break; + } + } + } + + FRFont returnFont = tmpStyle.getFRFont().applySize(fontScale ? newFRFont.getSize2D() : FRFont.convResolution(newFRFont.getSize2D(), resolution)); + tmpStyle.deriveFRFont(returnFont); + + return new ContentLineGroup(tmpStyle, newLineTextList, newTextHeight); + } + + private static List resovleLineTextListByStyle(Font font, List lineTextList, NewFormStyle tmpStyle, int width, int resolution) { + List newLineTextList = new ArrayList(); + + List tmpLineTextList; + for (int i = 0; i < lineTextList.size(); i++) { + tmpLineTextList = BaseUtils.getLineTextList((String) lineTextList.get(i), tmpStyle, font, width, resolution); + for (int j = 0; j < tmpLineTextList.size(); j++) { + newLineTextList.add((String) tmpLineTextList.get(j)); + } + } + + return newLineTextList; + } + + /** + * 修改格子样式 + * + * @param width 宽 k + * @param height 高g + * @param text 文本w + * @param style 样式y + * @param resolution 转化率z + * @return 样式 y + */ + public static Style modCellStyleString(int width, int height, String text, Style style, int resolution) { + List lineTextList = BaseUtils.getLineTextList(text, style, null, height, width, resolution); + if (style.getTextStyle() == NewFormStyle.TEXTSTYLE_SINGLELINEADJUSTFONT) { + style = singleLineAdjustFont(style, lineTextList, width, height, null, resolution, false).style; + } else if (style.getTextStyle() == NewFormStyle.TEXTSTYLE_MULTILINEADJUSTFONT) { + style = mulitiLineAdjustFont(style, lineTextList, width, height, null, resolution, false).style; + } + + return style; + } +} diff --git a/designer-form/src/main/java/com/fr/design/fit/common/PaintUtils.java b/designer-form/src/main/java/com/fr/design/fit/common/PaintUtils.java new file mode 100644 index 000000000..8f913a7aa --- /dev/null +++ b/designer-form/src/main/java/com/fr/design/fit/common/PaintUtils.java @@ -0,0 +1,835 @@ +/* + * Copyright(c) 2001-2010, FineReport Inc, All Rights Reserved. + */ +package com.fr.design.fit.common; + +import com.fr.base.AutoChangeLineAndDrawManager; +import com.fr.base.BaseFormula; +import com.fr.base.BaseUtils; +import com.fr.base.GraphHelper; +import com.fr.base.ImageProvider; +import com.fr.base.Painter; +import com.fr.base.Style; +import com.fr.base.Utils; +import com.fr.base.background.ColorBackground; +import com.fr.base.chart.BaseChartCollection; +import com.fr.base.chart.result.WebChartIDInfo; +import com.fr.code.BarcodeImpl; +import com.fr.code.bar.BarcodeException; +import com.fr.code.bar.core.BarCodeUtils; +import com.fr.code.bar.core.BarcodeAttr; +import com.fr.data.DataUtils; +import com.fr.data.PresentationType; +import com.fr.data.condition.ListCondition; +import com.fr.file.ResultChangeWhenExport; +import com.fr.form.ui.Widget; +import com.fr.general.Background; +import com.fr.general.ComparatorUtils; +import com.fr.general.FRFont; +import com.fr.general.ImageWithSuffix; +import com.fr.log.FineLoggerFactory; +import com.fr.plugin.ExtraClassManager; +import com.fr.report.cell.CellElement; +import com.fr.report.cell.FloatElement; +import com.fr.report.cell.TemplateCellElement; +import com.fr.report.cell.cellattr.CellExpandAttr; +import com.fr.report.cell.cellattr.CellGUIAttr; +import com.fr.report.cell.cellattr.core.CellUtils; +import com.fr.report.cell.cellattr.core.ResultSubReport; +import com.fr.report.cell.cellattr.core.group.DSColumn; +import com.fr.report.core.Html2ImageUtils; +import com.fr.report.core.ReportUtils; +import com.fr.script.Calculator; +import com.fr.stable.Constants; +import com.fr.stable.CoreConstants; +import com.fr.stable.StringUtils; +import com.fr.stable.bridge.ObjectHolder; +import com.fr.stable.fun.AutoChangeLineAndDrawProcess; +import com.fr.stable.fun.FontProcessor; +import com.fr.stable.html.Tag; +import com.fr.stable.unit.FU; +import com.fr.stable.unit.PT; +import com.fr.stable.unit.UNIT; +import com.fr.stable.web.Repository; + +import javax.swing.ImageIcon; +import java.awt.AlphaComposite; +import java.awt.Color; +import java.awt.Font; +import java.awt.FontMetrics; +import java.awt.Graphics2D; +import java.awt.Image; +import java.awt.Paint; +import java.awt.Rectangle; +import java.awt.font.TextAttribute; +import java.awt.geom.GeneralPath; +import java.awt.image.BufferedImage; +import java.util.Hashtable; +import java.util.List; +import java.util.Map; + +/** + * The util for paint. + */ +public class PaintUtils { + // Add by Denny + public static final int CELL_MARK_SIZE = 6; + public static final Color CELL_HIGHT_LIGHT_MARK_COLOR = new Color(255, 0, 55); + public static final Color CELL_PRESENT_MARK_COLOR = new Color(0, 255, 200); + public static final Color CELL_PAGINATION_MARK_COLOR = new Color(55, 255, 0); + public static final Color CELL_RESULT_MARK_COLOR = new Color(200, 0, 255); + public static final Color CELL_CONDITION_FILTER_MARK_COLOR = new Color(255, 200, 0); + public static final Color CELL_PARAMETER_FILTER_MARK_CONLR = new Color(0, 55, 255); + + public static final Color CELL_DIRECTION_MARK_COLOR = Color.gray; + private static final int UNIT_SIZE = 4; + + //原值是15,矩形线条会缺失,加1px绘制没问题。这地方有水印,但是貌似不是水印影响,未找到线条被挡住的原因 + private static final int WIDGET_WIDTH = 16; + private static final int WIDGET_HEIGHT = 16; + + // Suppresses default constructor, ensuring non-instantiability. + private PaintUtils() { + } + + // font attributes map cache + private static Hashtable fontAttributeMapCache = new Hashtable(); + + /* + * 用于在Grid里面画CellElement的Content + Background + * + * 不画Border,是因为在Grid里面先画所有单元格的Content + Background,再画所有单元格的Border(peter认为这可以提高速度) + */ + public static void paintGridCellContent(Graphics2D g2d, TemplateCellElement cell, int width, int height, int resolution) { + int cell_mark_size = CELL_MARK_SIZE; + // denny_Grid + // 左上角: 条件高亮, 形态 + int leftUpCount = 0; + int rightUpCount = 0; + int leftDownCount = 0; + GraphHelper.applyRenderingHints(g2d); + if (paintHighlightGroupMarkWhenExsit(g2d, cell, leftUpCount)) { + leftUpCount++; + } + if (paintPresentMarkWhenExsit(g2d, cell, leftUpCount)) { + leftUpCount++; + } + if (paintPaginationMarkWhenExsit(g2d, cell, width, rightUpCount)) { + rightUpCount++; + } + paintWidgetMarkWhenExsit(g2d, cell, width, height); + paintExpandMarkWhenExsit(g2d, cell); + Object value = cell.getValue(); + if (value == null) {// 先判断是否是空. + return; + } + if (paintResultMarkWhenExsit(g2d, value, width, rightUpCount)) { + rightUpCount++; + } + + if (paintDSColumnParametermarkWhenExsit(g2d, value, height, leftDownCount)) { + leftDownCount++; + } + + if (paintDSColumnConditionmarkWhenExsit(g2d, value, height, leftDownCount)) { + leftDownCount++; + } + // 画value,但因为是在Grid里面画,所以画Formula.content + if (value instanceof BaseFormula) { + value = ((BaseFormula) value).getContent(); + } + if (value instanceof ImageWithSuffix) { + value = ((ImageWithSuffix) value).getFineImage(); + } + if (value instanceof BaseChartCollection) { + value = ((BaseChartCollection) value).createResultChartPainterWithOutDealFormula(Calculator.createCalculator(), WebChartIDInfo.createEmptyDesignerInfo(), width, height); + } + // Carl:当是子报表时,在格子里画一个子报表的图 + /* + * alex:TODO 此处在Grid里面画ChartCollection和SubReport都只画一个图表,这种做法,很不雅 + */ + if (value instanceof ResultSubReport) { + value = BaseUtils.readImage("/com/fr/base/images/report/painter/subReport.png"); + GraphHelper.paintImage(g2d, width, height, (Image) value, Constants.IMAGE_CENTER, + BaseUtils.getAlignment4Horizontal(cell.getStyle(), value), cell.getStyle().getVerticalAlignment(), + width > 16 ? 16 : width, height > 16 ? 16 : height); + } else { + renderContent(g2d, value, cell.getStyle(), width, height, resolution); + } + } + + + private static void renderContent(Graphics2D g2d, Object value, Style style, int width, int height, int resolution) { + if (value != null && width != 0 && height != 0) { + if (style == null) { + style = Style.DEFAULT_STYLE.deriveImageLayout(1); + } + + if (value instanceof BaseFormula) { + value = ((BaseFormula) value).getResult(); + } + + if (value instanceof Painter) { + ((Painter)value).paint(g2d, width, height, resolution, style); + } else if (value instanceof ImageProvider) { + Style.paintImageContent(g2d, ((ImageProvider) value).getImage(), style, width, height, resolution); + } else if (value instanceof Image) { + Style.paintImageContent(g2d, (Image) value, style, width, height, resolution); + } else { + String var6 = Style.valueToText(value, style.getFormat()); + NewFormStyle.paintCellStyleString2(g2d, width, height, var6, style, resolution); + } + + } + } + + + private static boolean paintHighlightGroupMarkWhenExsit(Graphics2D g2d, TemplateCellElement cell, int left_up_count) { + if (cell.getHighlightGroup() != null && cell.getHighlightGroup().size() > 0) { + Paint oldPaint = g2d.getPaint(); + + g2d.setPaint(CELL_HIGHT_LIGHT_MARK_COLOR); + GeneralPath polyline = new GeneralPath(GeneralPath.WIND_EVEN_ODD, 3); + polyline.moveTo(0, 0); + polyline.lineTo(0, CELL_MARK_SIZE); + polyline.lineTo(CELL_MARK_SIZE, 0); + GraphHelper.fill(g2d, polyline); + + g2d.setPaint(oldPaint); + return true; + } + return false; + } + + private static boolean paintPresentMarkWhenExsit(Graphics2D g2d, TemplateCellElement cell, int left_up_count) { + if (cell.getPresent() != null) { + Paint oldPaint = g2d.getPaint(); + + g2d.setPaint(CELL_PRESENT_MARK_COLOR); + GeneralPath polyline = new GeneralPath(GeneralPath.WIND_EVEN_ODD, 3); + polyline.moveTo(0 + left_up_count * CELL_MARK_SIZE, 0); + polyline.lineTo(0 + left_up_count * CELL_MARK_SIZE, 6); + polyline.lineTo(CELL_MARK_SIZE + left_up_count * CELL_MARK_SIZE, 0); + GraphHelper.fill(g2d, polyline); + + g2d.setPaint(oldPaint); + return true; + } + return false; + } + + private static boolean paintPaginationMarkWhenExsit(Graphics2D g2d, TemplateCellElement cell, int width, int ringt_up_count) { + // 右上角: 标记是否有分页 + if (isRightTopMarker(cell)) { + Paint oldPaint = g2d.getPaint(); + + g2d.setPaint(CELL_PAGINATION_MARK_COLOR); + GeneralPath polyline = new GeneralPath(GeneralPath.WIND_EVEN_ODD, 3); + polyline.moveTo(width - 1 - ringt_up_count * CELL_MARK_SIZE, 0); + polyline.lineTo(width - 1 - ringt_up_count * CELL_MARK_SIZE, CELL_MARK_SIZE); + polyline.lineTo(width - 1 - (ringt_up_count + 1) * CELL_MARK_SIZE, 0); + GraphHelper.fill(g2d, polyline); + + g2d.setPaint(oldPaint); + return true; + } + return false; + } + + private static boolean isRightTopMarker(TemplateCellElement cell) { + return cell.getCellPageAttr() != null && (cell.getCellPageAttr().isPageAfterColumn() + || cell.getCellPageAttr().isPageBeforeColumn() + || cell.getCellPageAttr().isPageAfterRow() + || cell.getCellPageAttr().isPageBeforeRow()); + } + + private static boolean paintResultMarkWhenExsit(Graphics2D g2d, Object value, int width, int ringt_up_count) { + //右上角标记是否自定义显示 + if (value instanceof DSColumn && ((DSColumn) value).getResult() != null) { + if (!ComparatorUtils.equals(((DSColumn) value).getResult(), "$$$")) { + Paint oldPaint = g2d.getPaint(); + + g2d.setPaint(CELL_RESULT_MARK_COLOR); + GeneralPath polyline = new GeneralPath(GeneralPath.WIND_EVEN_ODD, 3); + polyline.moveTo(width - ringt_up_count * CELL_MARK_SIZE - 1, 0); + polyline.lineTo(width - ringt_up_count * CELL_MARK_SIZE - 1, CELL_MARK_SIZE); + polyline.lineTo(width - (ringt_up_count + 1) * CELL_MARK_SIZE - 1, 0); + GraphHelper.fill(g2d, polyline); + + g2d.setPaint(oldPaint); + return true; + } + } + + return false; + } + + private static void paintWidgetMarkWhenExsit(Graphics2D g2d, TemplateCellElement cell, int width, int height) { + // 右下角:是否填报, 设置为4时,三角太小了,不知何故,设置为6 + if (cell.getWidget() != null) { + Widget widget = cell.getWidget(); + Image img = ((ImageIcon) ReportUtils.createWidgetIcon(widget.getClass())).getImage(); + g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_ATOP, 0.67f)); + g2d.drawImage(img, width - 15, height - 15, WIDGET_WIDTH, WIDGET_HEIGHT, null); + g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER)); + } + } + + private static void paintExpandMarkWhenExsit(Graphics2D g2d, TemplateCellElement cell) { + CellExpandAttr cellExpandAttr = cell.getCellExpandAttr(); + if (cellExpandAttr != null) { + if (cellExpandAttr.getDirection() == Constants.TOP_TO_BOTTOM) { + Paint oldPaint = g2d.getPaint(); + g2d.setPaint(CELL_DIRECTION_MARK_COLOR); + GraphHelper.drawLine(g2d, 2, 0, 2, 5); + GraphHelper.drawLine(g2d, 2, 5, 0, 2); + GraphHelper.drawLine(g2d, 2, 5, 4, 2); + g2d.setPaint(oldPaint); + } else if (cellExpandAttr.getDirection() == Constants.LEFT_TO_RIGHT) { + Paint oldPaint = g2d.getPaint(); + g2d.setPaint(CELL_DIRECTION_MARK_COLOR); + GraphHelper.drawLine(g2d, 0, 2, 5, 2); + GraphHelper.drawLine(g2d, 5, 2, 2, 0); + GraphHelper.drawLine(g2d, 5, 2, 2, 4); + g2d.setPaint(oldPaint); + } + } + } + + private static boolean paintDSColumnConditionmarkWhenExsit(Graphics2D g2d, Object value, int height, int left_dowm_count) { + // 左下角:数据列(DSColumn)相关:比如条件过滤 + if (value instanceof DSColumn && ((DSColumn) value).getCondition() != null) { + if (((DSColumn) value).getCondition() instanceof ListCondition && + ((ListCondition) ((DSColumn) value).getCondition()).getJoinConditionCount() == 0) { + // do nothing + } else { + Paint oldPaint = g2d.getPaint(); + + g2d.setPaint(CELL_CONDITION_FILTER_MARK_COLOR); + GeneralPath polyline = new GeneralPath(GeneralPath.WIND_EVEN_ODD, 3); + polyline.moveTo(0 + left_dowm_count * CELL_MARK_SIZE, height - 1); + polyline.lineTo((left_dowm_count + 1) * CELL_MARK_SIZE + 1, height - 1); + polyline.lineTo(0 + left_dowm_count * CELL_MARK_SIZE, height - 2 - CELL_MARK_SIZE); + GraphHelper.fill(g2d, polyline); + + g2d.setPaint(oldPaint); + return true; + } + } + return false; + } + + private static boolean paintDSColumnParametermarkWhenExsit(Graphics2D g2d, Object value, int height, int left_dowm_count) { + // 左下角:动态注入参数 + if (value instanceof DSColumn && ((DSColumn) value).getParameters() != null) { + if (((DSColumn) value).getParameters().length > 0) { + Paint oldPaint = g2d.getPaint(); + + g2d.setPaint(CELL_PARAMETER_FILTER_MARK_CONLR); + GeneralPath polyline = new GeneralPath(GeneralPath.WIND_EVEN_ODD, 3); + polyline.moveTo(0 + left_dowm_count * CELL_MARK_SIZE, height - 1); + polyline.lineTo((left_dowm_count + 1) * CELL_MARK_SIZE + 1, height - 1); + polyline.lineTo(0 + left_dowm_count * CELL_MARK_SIZE, height - 2 - CELL_MARK_SIZE); + GraphHelper.fill(g2d, polyline); + + g2d.setPaint(oldPaint); + return true; + } + } + return false; + } + + /* + * 画悬浮元素 + * + * 仅根据宽度 + 高度画 + */ + //b:此方法在grid和tohtml的时候都被调用,所以对formula会有冲突,暂时这么改,应该考虑分开的,也可以根据result来判断,但是那么写好像不妥 + public static void paintFloatElement(Graphics2D g2d, FloatElement flotEl, int width, int height, int resolution) { + Style.paintBackground(g2d, flotEl.getStyle(), width, height); + + Object value = flotEl.getValue(); + if (value instanceof BaseFormula) { + value = ((BaseFormula) value).getContent(); + } + if (value instanceof BaseChartCollection) { + value = ((BaseChartCollection) value).createResultChartPainterWithOutDealFormula(Calculator.createCalculator(), WebChartIDInfo.createEmptyDesignerInfo(), width, height); + } + //图片需要切割一下 + if (value instanceof Image) { + value = CellUtils.value2ImageWithBackground(value, resolution, flotEl.getStyle(), width, height); + } + Style.paintContent(g2d, value, flotEl.getStyle(), width, height, resolution); + + Style.paintBorder(g2d, flotEl.getStyle(), width, height); + } + + /* + * 画悬浮元素flotEl + * + * 也就是画三个东西:背景 + 内容 + 边框 + */ + public static void paintFloatElement(Graphics2D g2d, FloatElement flotEl, Rectangle paintRectangle, Rectangle clipRectangle, int resolution) { + // 画悬浮元素的背景 + Style.paintBackground(g2d, flotEl.getStyle(), paintRectangle, clipRectangle); + + Object value = flotEl.getValue(); + if (value instanceof ResultChangeWhenExport) { + value = ((ResultChangeWhenExport) value).changeThis(); + } + // 画悬浮元素的内容 + Style.paintContent(g2d, value, resolution, flotEl.getStyle(), paintRectangle, clipRectangle); + // 画悬浮元素的边框 + Style.paintBorder(g2d, flotEl.getStyle(), paintRectangle, clipRectangle); + } + + public static void paintHTMLContent(Graphics2D g2d, String value, int resolution, Style style, Rectangle paintRectangle, Rectangle clipRectangle) { + Style.paintContent(g2d, createHTMLContentBufferedImage(value, paintRectangle, 0, 0, style), resolution, style, paintRectangle, clipRectangle); + } + + public static void paintTag(Painter painter, Repository repo, int width, int height, Style style, Tag tag) { + painter.paintTag(repo, width, height, style, tag); + } + + /** + * 如果用户希望以HTML方式展示String,这个时候先value变成图片 + * + * @param value 值 + * @param paintRectangle 绘制范围 + * @param x x坐标 + * @param y y坐标 + * @param style 当前格子样式 + * @return BufferedImage 返回图片. + */ + public static BufferedImage createHTMLContentBufferedImage(String value, Rectangle paintRectangle, int x, int y, Style style) { + return Html2ImageUtils.createHTMLContentBufferedImage(value, paintRectangle, x, y, style); + } + + /** + * see BaseUtils.getLineTextList, 等于BaseUtils.getLineTextList().size() + * Denny: 为了提高速度和性能,才单独拿出来的 + * TODO: 重构 + * + * @param text 文本 + * @param style 样式 + * @param paintWidth 单元格宽度 + * @return paintWidth 单位为PT + */ + public static int getLineTextCount(String text, Style style, UNIT paintWidth) { + if (style.getRotation() != 0) { + return 1; + } + + + if (style.getTextStyle() != Style.TEXTSTYLE_WRAPTEXT) { + return dealNotWrapTextCount(text.toCharArray()); + } else {// 自动换行 + return dealWrapTextCount(text, style, paintWidth); + } + } + + private static int dealNotWrapTextCount(char[] text_chars) { + boolean remain_chars = false; + int count = 0; + for (int t = 0; t < text_chars.length; t++) { + if (text_chars[t] == '\\') {// 判断是否是 "\n" + if (t + 1 < text_chars.length && text_chars[t + 1] == 'n') { + // 是"\n"字符串,但不是换行符. + t++; + count++; + if (remain_chars) { + remain_chars = false; + } + } else { + if (!remain_chars) { + remain_chars = true; + } + } + } else if (text_chars[t] == '\n' || (text_chars[t] == '\r' && t + 1 < text_chars.length - 1 && text_chars[t + 1] != '\n')) { + count++; + if (remain_chars) { + remain_chars = false; + } + } else { + if (!remain_chars) { + remain_chars = true; + } + } + } + + // 最后一个 + if (remain_chars) { + count++; + } + + return count; + } + + // 自动换行 + //neil:style里面, 默认值padding right = 2时, 默认不生效, 这边算行高时也不要计算入内 + //临时处理, 去掉左边框线, 因为浏览器计算时需要考虑左边框线宽度, 但这边还是存在问题的 + //同样需要考虑的是导出和web端展示, padding计算方式也不一致. + private static int dealWrapTextCount(String text, Style style, UNIT unitWidth) { + AutoChangeLineAndDrawProcess process = AutoChangeLineAndDrawManager.getProcess(); + if (process != null) { + return process.getAutoChangeLineCount(text, new ObjectHolder(style), unitWidth); + } + int count = 0; + char[] text_chars = text.toCharArray(); + FontMetrics fontMetrics = getFontMetrics(style); + double paintWidth = unitWidth.toPixD(Constants.FR_PAINT_RESOLUTION); + double width = paintWidth - style.getPaddingLeft() - (style.getPaddingRight() == Style.DEFAULT_PADDING ? 0 : style.getPaddingRight()) - style.getBorderLeftWidth(); + boolean remain_lineText = false; + int lineTextWidth = 0; + int wordWidth = 0; + for (int t = 0, len = text_chars.length; t < len; t++) { + if (t != 0 && BaseUtils.isNumOrLetter(text_chars[t]) && BaseUtils.isNumOrLetter(text_chars[t - 1])) { + if (wordWidth + fontMetrics.charWidth(text_chars[t]) > width) { + if (lineTextWidth > 0) { + count++; + remain_lineText = false; + lineTextWidth = 0; + } + count++; + wordWidth = 0; + } + wordWidth += fontMetrics.charWidth(text_chars[t]); + } else if (isSwitchLine(text_chars, t) || isLN(text_chars, t)) {// 判断是否是 "\n" + if (isLN(text_chars, t)) { + t++;// 忽略'n'字符.// 是"\n"字符串,但不是换行符,依然需要换行. + } + if (lineTextWidth + wordWidth > width && remain_lineText) { + count += 2; + } else { + count++; + } + remain_lineText = false; + lineTextWidth = 0; + wordWidth = 0; + } else { + if (text_chars[t] == '\\' && t + 1 < text_chars.length && text_chars[t + 1] == '\\') {// 判断是否是转义字符'\' + t++;// _denny: 增加了转义字符'\\'用来表示\,使"\n"可以输入 + } + if (lineTextWidth + wordWidth > width && remain_lineText) { + count++; + lineTextWidth = isPunctuationAtLineHead(t, text_chars) ? dealLineWidthWithPunctuation(t, text_chars, fontMetrics) : 0; + } + remain_lineText = true; + lineTextWidth += wordWidth; + wordWidth = fontMetrics.charWidth(text_chars[t]); + } + } + if (lineTextWidth + wordWidth > width && remain_lineText) { + count += 2; + } else { + count++; + } + return count; + } + + /** + * 标点符号是否在换行后的行首 + */ + private static boolean isPunctuationAtLineHead(int t, char[] text_chars) { + if (t > 1 && BaseUtils.isPunctuation(text_chars[t - 1])) { + return true; + } + return false; + } + + /** + * 防止有连续多个标点符号,要找一个非标点符号字符 + * + * @date 2014-4-17 + */ + private static int dealLineWidthWithPunctuation(int t, char[] text_chars, FontMetrics fontMetrics) { + if (t < 2) { + return 0; + } + int lineWidth = 0; + for (int index = t - 2; index >= 0; index--) { + lineWidth += fontMetrics.charWidth(text_chars[index]); + if (!BaseUtils.isPunctuation(text_chars[index])) { + break; + } + } + return lineWidth; + } + + private static boolean isSwitchLine(char[] text_chars, int t) { + return text_chars[t] == '\n' || (text_chars[t] == '\r' && t + 1 < text_chars.length - 1 && text_chars[t + 1] != '\n'); + } + + private static boolean isLN(char[] text_chars, int t) { + return text_chars[t] == '\\' && t + 1 < text_chars.length && text_chars[t + 1] == 'n'; + } + + /** + * Gets the preferred width. + */ + public static UNIT getPreferredWidth(CellElement cell, UNIT height) { + if (cell == null) { + return UNIT.ZERO; + } + + Object value = cell.getShowValue(); + // 只接受Text,Number,和SeparatorPainter + // got the text + if (value instanceof BaseFormula) { + if (((BaseFormula) value).getResult() != null) { + value = ((BaseFormula) value).getResult(); + } else { + value = StringUtils.EMPTY; + } + } + Style style = cell.getStyle(); + if (style == null) { + style = Style.DEFAULT_STYLE; + } + CellGUIAttr cg = cell.getCellGUIAttr() == null ? new CellGUIAttr() : cell.getCellGUIAttr(); + value = Utils.resolveOtherValue(value, cg.isShowAsImage(), PresentationType.EXPORT); + String text = Style.valueToText(value, style.getFormat()); + + FontMetrics cellFM = getFontMetrics(style); + //bug 12151 有边框线的单元格 自动调整列宽 会多一行 + UNIT padding = new PT(style.getPaddingLeft() + style.getPaddingRight()); + + if (cg.isShowAsHTML()) { + return Html2ImageUtils.getHtmlWidth(text, height, style); + } + + return FU.valueOfPix(cellFM.stringWidth(text) + UNIT_SIZE, Constants.FR_PAINT_RESOLUTION).add(padding); + } + + private static FontMetrics getFontMetrics(Style style) { + Font font = style.getFRFont().applyResolutionNP(Constants.FR_PAINT_RESOLUTION); + FontProcessor processor = ExtraClassManager.getInstance().getSingle(FontProcessor.MARK_STRING); + if (processor != null) { + font = processor.readExtraFont(font); + } + return GraphHelper.getFontMetrics(font); + } + + /** + * Preferred height. (Got the shrink preferred height of CellElement). + * 单位格的预计算高度 + * + * @param cellElement 单元格内容 + * @param paintWidth 画的宽度 + * @return UNIT 单位 + */ + public static UNIT analyzeCellElementPreferredHeight(CellElement cellElement, UNIT paintWidth) { + // 计算高度用显示值 + Object value = cellElement.getShowValue(); + // 只接受Text,Number,和SeparatorPainter + Style style = cellElement.getStyle(); + // got the text + if (value instanceof BaseFormula) { + if (((BaseFormula) value).getResult() != null) { + value = ((BaseFormula) value).getResult(); + } else { + value = StringUtils.EMPTY; + } + } + CellGUIAttr cg = cellElement.getCellGUIAttr() == null ? new CellGUIAttr() : cellElement.getCellGUIAttr(); + if (!(value instanceof String) && !(value instanceof Integer)) { + value = DataUtils.resolveOtherValue(value, cg.isShowAsImage(), cg.isShowAsDownload(), null, true); + } + String text = Style.valueToText(value, style.getFormat()); + + if (cg.isShowAsHTML()) { + return Html2ImageUtils.getHtmlHeight(text, paintWidth, style); + } + + return PaintUtils.analyzeCellElementPreferredHeight(text, style, paintWidth, cg.isShowAsHTML()); + } + + /** + * 单位格的预计算高度 + * 单位PT + * + * @param text 文本 + * @param style 格式 + * @param paintWidth 画的宽度 + * @param isShowAsHtml 是否以html展示 + * @return 返回 单位 + */ + private static UNIT analyzeCellElementPreferredHeight(String text, Style style, UNIT paintWidth, boolean isShowAsHtml) { + if (style == null) { + //peter:获取默认的Style. + style = Style.DEFAULT_STYLE; + } + + // got the text + if (text == null || text.length() <= 0) { + return PT.valueOf(0); + } + + // 变成Line Text List. + if (style.getRotation() != 0) { // more easy to paint. + // attribute map. + return PT.valueOf((float) GraphHelper.stringDimensionWithRotation(text, style.getFRFont(), -style.getRotation(), + CoreConstants.DEFAULT_FRC).getHeight()); + } + // 先获得FontMetics. + int lineCount = getLineTextCount(text, style, paintWidth); + AutoChangeLineAndDrawProcess process = AutoChangeLineAndDrawManager.getProcess(); + if (process != null) { + //算了这两个接口分开做 + return process.getLinedTextHeight(lineCount, new ObjectHolder(style)); + } + + // carl:和paint那边一致,添上段前段后和行间距 + PT lineSpacing = PT.valueOf(style.getSpacingAfter() + style.getSpacingBefore() + style.getLineSpacing() * lineCount); + FontMetrics fontMetrics = getFontMetrics(style); + int textHeight = fontMetrics.getHeight(); + FU allTextHeight = FU.valueOfPix(textHeight * lineCount, Constants.FR_PAINT_RESOLUTION); + return lineSpacing.add(allTextHeight);// 需要给底部添加Leading. + } + + /** + * 截取文字,只考虑了垂直方向,水平方向没意义且难度大. + * + * @param value 画的值 + * @param style 字体样式格式. + * @param blockArea 冻结的范围 + * @param resolution 分辨率 + * @return 返回的字符串 + */ + public static String clipBlockValue(Object value, Style style, Rectangle primitiveArea, Rectangle blockArea, int resolution, boolean isShowAsHTML) { + if (value == null) { + return null; + } + if (value instanceof BaseFormula) { + value = ((BaseFormula) value).getResult(); + } + if (blockArea.y >= primitiveArea.height || blockArea.y + blockArea.height <= 0) { + return null; + } + //截取位置,相对于clipArea + int startY = blockArea.y > 0 ? blockArea.y : 0; + int endY = blockArea.y + blockArea.height < primitiveArea.height ? blockArea.y + blockArea.height : primitiveArea.height; + if (blockArea.x >= primitiveArea.width || blockArea.x + blockArea.width <= 0) { + return null; + } + if (isShowAsHTML) { + return Html2ImageUtils.clipHtmlContent(value, style, primitiveArea, resolution, startY, endY); + } + List lineList = BaseUtils.getLineTextList((String) value, style, style.getFRFont().applyResolutionNP(resolution), primitiveArea.width, resolution); + if (lineList.isEmpty()) { + return null; + } + double spacingBefore = PT.pt2pix(style.getSpacingBefore(), resolution); + double spacingAfter = PT.pt2pix(style.getSpacingAfter(), resolution); + double lineSpacing = PT.pt2pix(style.getLineSpacing(), resolution); + double lineHeight = lineSpacing + GraphHelper.getFontMetrics(style.getFRFont().applyResolutionNP(resolution)).getHeight(); + int textAllHeight = (int) (lineHeight * lineList.size() + spacingBefore + spacingAfter); + //第一行文字距区域高度 + int textStartY = (int) spacingBefore; + + if (style.getVerticalAlignment() == Constants.BOTTOM) { + textStartY += (primitiveArea.height - textAllHeight); + } + if (endY <= textStartY || startY >= textStartY + lineHeight * lineList.size()) { + return null; + } + int lineStart = getLineStart(lineList, lineHeight, textStartY, startY);//截取区域起始行 + int lineEnd = getLineEnd(lineList, lineHeight, endY, textStartY);//截取区域结束行 + String text = ""; + for (; lineStart <= lineEnd; lineStart++) { + text += lineList.get(lineStart); + } + return text; + } + + private static int getLineStart(List lineList, double lineHeight, int textStartY, int startY) { + int lineStart = 0; + for (int i = 0; i < lineList.size(); i++) { + if (textStartY + lineHeight * (i) <= startY && textStartY + lineHeight * (i + 1) > startY) {//压线 + if (startY - textStartY - lineHeight * (i) > lineHeight / 2) { + lineStart = i + 1; + } else { + lineStart = i; + } + } + } + return lineStart; + } + + private static int getLineEnd(List lineList, double lineHeight, int endY, int textStartY) { + int lineEnd = lineList.size() - 1; + for (int i = 0; i < lineList.size(); i++) { + if (textStartY + lineHeight * (i) < endY && textStartY + lineHeight * (i + 1) >= endY) {//压线 + //neil:仿宋,12号字, 行间距8为例, 转为px的行间距大小为10.666, 这边算出的应该有31.98行, 因此要进位 + if (endY - textStartY - lineHeight * (i) >= lineHeight / 2) { + lineEnd = i; + } else { + lineEnd = i - 1; + } + } + } + return lineEnd; + } + + /** + * paintBarcode + */ + public static void paintBarcode(Graphics2D g2d, int width, int height, String text, Style style, BarcodeAttr barcodeAttr) { + BarcodeImpl barcodeImpl; + try { + barcodeImpl = BarCodeUtils.getBarcodeImpl(barcodeAttr, text); + } catch (BarcodeException exp) { + try { + //设置默认值. + barcodeImpl = BarCodeUtils.getBarcodeImpl(new BarcodeAttr(), null); + } catch (BarcodeException e) { + FineLoggerFactory.getLogger().error(e.getMessage(), e); + return; + } + } + + //字体 + if (style.getFRFont() != null) { + barcodeImpl.setFont(style.getFRFont()); + barcodeImpl.setForeground(style.getFRFont().getForeground()); + } + //背景 + Background background = style.getBackground(); + if (background != null && background instanceof ColorBackground) { + barcodeImpl.setBackground(((ColorBackground) background).getColor()); + } + + //根据宽度和高度来确定起始点 + int pointX = (width - barcodeImpl.getWidth()) / 2; + int pointY = (height - barcodeImpl.getHeight()) / 2; + + barcodeImpl.draw(g2d, pointX, pointY); + } + + /** + * create font attribute map, 创建属性map + * + * @param font 字体 + * @return map 返回字体创建的Map + */ + public static Map createFontAttributeMap(Font font) { + Map returnFontAttributeMap = (Map) fontAttributeMapCache.get(font); + if (returnFontAttributeMap == null) {// create + // returnFontAttributeMap. + returnFontAttributeMap = font.getAttributes(); + fontAttributeMapCache.put(font, returnFontAttributeMap); + } + + if (font instanceof FRFont) { + FRFont frFont = (FRFont) font; + + // Strikethrough + if (frFont.isStrikethrough()) { + returnFontAttributeMap.put(TextAttribute.STRIKETHROUGH, TextAttribute.STRIKETHROUGH_ON); + } + } + + return returnFontAttributeMap; + } + +} diff --git a/designer-form/src/main/java/com/fr/design/fit/common/TemplateTool.java b/designer-form/src/main/java/com/fr/design/fit/common/TemplateTool.java new file mode 100644 index 000000000..c760d01da --- /dev/null +++ b/designer-form/src/main/java/com/fr/design/fit/common/TemplateTool.java @@ -0,0 +1,214 @@ +package com.fr.design.fit.common; + +import com.fr.design.designer.beans.events.DesignerEvent; +import com.fr.design.designer.creator.XLayoutContainer; +import com.fr.design.designer.creator.XWAbsoluteBodyLayout; +import com.fr.design.designer.creator.XWFitLayout; +import com.fr.design.file.HistoryTemplateListCache; +import com.fr.design.file.MutilTempalteTabPane; +import com.fr.design.fit.DesignerUIModeConfig; +import com.fr.design.fit.JFormType; +import com.fr.design.fit.NewJForm; +import com.fr.design.mainframe.DesignerContext; +import com.fr.design.mainframe.FormArea; +import com.fr.design.mainframe.FormDesigner; +import com.fr.design.mainframe.JTemplate; +import com.fr.event.Event; +import com.fr.event.Listener; +import com.fr.file.MemFILE; +import com.fr.file.StashedFILE; +import com.fr.form.fit.common.LightTool; +import com.fr.form.fit.NewFormMarkAttr; +import com.fr.form.ui.Widget; +import com.fr.form.ui.widget.CRBoundsWidget; +import com.fr.general.ComparatorUtils; + +import java.awt.Rectangle; +import java.util.List; + +/** + * @description:模板工具类 + * @author: Henry.Wang + * @create: 2020/09/03 14:19 + */ +public class TemplateTool { + //和转换有关的所有代码都在这个监听器中 + //在判断新老模式时,统一使用FormUIModeConfig.getInstance().newUIMode()进行判断 + private static Listener switchListener = new Listener() { + @Override + public void on(Event event, JTemplate jTemplate) { + JFormType currentType = JFormType.OLD_TYPE; + if (AdaptiveSwitchUtil.isSwitchJFromIng()) { + currentType = DesignerUIModeConfig.getInstance().newUIMode() ? JFormType.NEW_TYPE : JFormType.OLD_TYPE; + } else { + if (jTemplate instanceof NewJForm) { + NewJForm newJForm = (NewJForm) jTemplate; + if (newJForm.mobileForm()) { + currentType = JFormType.OLD_TYPE; + } else if (LightTool.containNewFormFlag(newJForm.getTarget()) || newJForm.getTarget().getTemplateID() == null) { + currentType = JFormType.NEW_TYPE; + } + } + } + //UI转换 + currentType.switchUIMode(); + //标志位转换 + currentType.updateJFromTemplateType(jTemplate); + //预览方式转换 + currentType.updatePreviewType(jTemplate); + } + }; + + public static Listener getSwitchListener() { + return switchListener; + } + + /** + * @param + * @Description: 获取当前模板 + * @return: + * @Author: Henry.Wang + * @date: 2020/9/6 14:17 + */ + public static JTemplate getCurrentEditingTemplate() { + JTemplate jTemplate = HistoryTemplateListCache.getInstance().getCurrentEditingTemplate(); + return jTemplate; + } + + /** + * @param + * @Description: 获取当前NewJForm类型的模板 + * @return: + * @Author: Henry.Wang + * @date: 2020/9/6 14:17 + */ + public static NewJForm getCurrentEditingNewJForm() { + JTemplate jTemplate = getCurrentEditingTemplate(); + if (jTemplate instanceof NewJForm) + return (NewJForm) jTemplate; + return null; + } + + /** + * @param newJTemplate + * @Description: 每次切换设计模式,都会生成新的JForm对象。但是TabPane关联的还是老的JForm对象,所以要重置关联的对象 + * @return: + * @Author: Henry.Wang + * @date: 2020/9/6 14:15 + */ + public static void resetTabPaneEditingTemplate(JTemplate newJTemplate) { + if (newJTemplate == null) + return; + List> jTemplateList = HistoryTemplateListCache.getInstance().getHistoryList(); + for (int i = 0; i < jTemplateList.size(); i++) { + JTemplate oldJTemplate = jTemplateList.get(i); + if (oldJTemplate != null && ComparatorUtils.equals(oldJTemplate.getEditingFILE(), newJTemplate.getEditingFILE())) { + jTemplateList.set(i, newJTemplate); + MutilTempalteTabPane.getInstance().refreshOpenedTemplate(jTemplateList); + return; + } + } + } + + + /** + * @param jTemplate + * @Description: 是否是新建的且没有操作的模板 + * @return: + * @Author: Henry.Wang + * @date: 2020/9/14 20:41 + */ + public static boolean isNewTemplateWithoutModify(JTemplate jTemplate) { + return ((jTemplate.getEditingFILE() instanceof MemFILE) && jTemplate.isSaved()) ? true : false; + } + + /** + * @param template + * @Description: 激活模板 + * @return: + * @Author: Henry.Wang + * @date: 2020/9/6 14:16 + */ + public static void activeAndResizeTemplate(JTemplate template) { + DesignerContext.getDesignerFrame().addAndActivateJTemplate(template); + DesignerContext.getDesignerFrame().setVisible(true); + DesignerContext.getDesignerFrame().resizeFrame(); + } + + /** + * @param jTemplate + * @Description:保存模板 + * @return: + * @Author: Henry.Wang + * @date: 2020/9/6 14:32 + */ + public static void saveForm(JTemplate jTemplate) { + //为空不保存 + if (jTemplate == null) + return; + //不是NewJForm的模板不保存 + if (!(jTemplate instanceof NewJForm)) + return; + //切换环境时产生的模板不保存 + if (jTemplate.getEditingFILE() instanceof StashedFILE) + return; + //新建的且没有操作的模板不保存 + if (TemplateTool.isNewTemplateWithoutModify(jTemplate)) + return; + jTemplate.stopEditing(); + jTemplate.saveTemplate(); + jTemplate.requestFocus(); + } + + /** + * @param height + * @param width + * @param newJForm + * @Description: 只改变绝对布局body的尺寸 + * @return: + * @Author: Henry.Wang + * @date: 2020/9/11 15:14 + */ + public static void onlyChangeAbsoluteBodySize(int height, int width, NewJForm newJForm) { + FormDesigner formDesigner = newJForm.getFormDesign(); + FormArea formArea = formDesigner.getArea(); + XLayoutContainer root = formDesigner.getRootComponent(); + XWAbsoluteBodyLayout xwAbsoluteBodyLayout = LayoutTool.getXWAbsoluteBodyLayout(newJForm); + if (root.acceptType(XWFitLayout.class) && xwAbsoluteBodyLayout != null) { + XWFitLayout layout = (XWFitLayout) root; + if (height == layout.toData().getContainerHeight() && width == layout.toData().getContainerWidth()) + return; + formArea.setWidthPaneValue(width); + formArea.setHeightPaneValue(height); + layout.setSize(width, height); + xwAbsoluteBodyLayout.setSize(width, height); + Widget widget = layout.toData().getWidget(0); + if (widget instanceof CRBoundsWidget) { + ((CRBoundsWidget) widget).setBounds(new Rectangle(0, 0, width, height)); + } + layout.toData().setContainerWidth(width); + layout.toData().setContainerHeight(height); + formArea.doReCalculateRoot(width, height, layout); + } + formDesigner.getEditListenerTable().fireCreatorModified(DesignerEvent.CREATOR_EDITED); + } + + /** + * @param newJForm + * @Description: 因为新老模板切换有截断的问题,所以body的尺寸会在调用onlyChangeAbsoluteBodySize之后发生改变。为了使模板文件中的尺寸不发生,要在模板保存时调用此函数 + * @return: + * @Author: Henry.Wang + * @date: 2020/9/11 15:20 + */ + public static void resetAbsoluteBodySize(NewJForm newJForm) { + if (LayoutTool.absoluteLayoutForm(newJForm)) { + NewFormMarkAttr newFormMarkAttr = newJForm.getTarget().getAttrMark(NewFormMarkAttr.XML_TAG); + XLayoutContainer root = newJForm.getFormDesign().getRootComponent(); + if (newFormMarkAttr != null && root instanceof XWFitLayout) { + ((XWFitLayout) root).toData().setContainerWidth(newFormMarkAttr.getBodyWidth()); + ((XWFitLayout) root).toData().setContainerHeight(newFormMarkAttr.getBodyHeight()); + } + } + } + +} diff --git a/designer-form/src/main/java/com/fr/design/fit/menupane/BrowserFitAttrPane.java b/designer-form/src/main/java/com/fr/design/fit/menupane/BrowserFitAttrPane.java new file mode 100644 index 000000000..fdc2f8332 --- /dev/null +++ b/designer-form/src/main/java/com/fr/design/fit/menupane/BrowserFitAttrPane.java @@ -0,0 +1,303 @@ +package com.fr.design.fit.menupane; + +import com.fr.config.Configuration; +import com.fr.design.beans.BasicBeanPane; +import com.fr.design.fit.FitStateCompatible; +import com.fr.design.fit.JFormType; +import com.fr.form.fit.FitType; +import com.fr.form.fit.config.FormFitConfig; +import com.fr.design.gui.ibutton.UIButton; +import com.fr.design.gui.ibutton.UIRadioButton; +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.layout.TableLayout; +import com.fr.design.layout.TableLayoutHelper; +import com.fr.report.fit.ReportFitAttr; +import com.fr.stable.StringUtils; +import com.fr.transaction.Configurations; +import com.fr.transaction.Worker; + +import javax.swing.BorderFactory; +import javax.swing.ButtonGroup; +import javax.swing.JPanel; +import javax.swing.SwingConstants; +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.Component; +import java.awt.Cursor; +import java.awt.FlowLayout; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; + +/** + * Created by Administrator on 2016/5/5/0005. + */ +public class BrowserFitAttrPane extends BasicBeanPane { + + protected FontRadioGroup fontRadioGroup; + protected FitRadioGroup fitRadionGroup; + protected UICheckBox globalCheck; + protected FitPreviewPane fitPreviewPane; + protected ReportFitAttr localFitAttr; + protected UIRadioButton horizonRadio; + protected UIRadioButton doubleRadio; + protected UIRadioButton notFitRadio; + protected UIRadioButton fontFitRadio; + protected UIRadioButton fontNotFitRadio; + private UIButton editGlobalOps; + private JPanel borderPane; + private JPanel globalOpsPane; + private JPanel fitOpsPane; + private final JFormType jFormType; + + public BrowserFitAttrPane(JFormType jFormType, ReportFitAttr fitAttr) { + this.jFormType = jFormType; + if (fitAttr == null) { + initComponents(jFormType.obtainFitAttr()); + } else { + initComponents(fitAttr); + } + } + + protected void initComponents(ReportFitAttr globalFitAttr) { + this.setLayout(FRGUIPaneFactory.createBorderLayout()); + globalOpsPane = initGlobalOpsPane(globalFitAttr); + this.add(globalOpsPane, BorderLayout.NORTH); + fitOpsPane = initFitOpsPane(); + } + + protected void initBorderPane(String title) { + borderPane = FRGUIPaneFactory.createTitledBorderPaneCenter(title); + borderPane.setLayout(new FlowLayout(FlowLayout.LEFT, 10, 0)); + borderPane.add(fitOpsPane, BorderLayout.WEST); + fitPreviewPane = new FitPreviewPane(); + borderPane.add(fitPreviewPane, BorderLayout.SOUTH); + borderPane.add(getHintUILabel()); + this.add(borderPane, BorderLayout.CENTER); + } + + /** + * @Description: 获取提示信息 + * @param + * @return: + * @Author: Henry.Wang + * @date: 2020/9/9 16:09 + */ + private UILabel getHintUILabel() { + String hint = globalCheck.isSelected() ? getFillBlank(3) + Toolkit.i18nText("Fine-Designer_Fit_Attr_Pane_Hint") : StringUtils.EMPTY; + UILabel hintUILabel = new UILabel(hint); + hintUILabel.setForeground(new Color(143, 143, 146)); + return hintUILabel; + } + + /** + * @Description: 获取空字符串 + * @param n + * @return: + * @Author: Henry.Wang + * @date: 2020/9/9 16:09 + */ + private String getFillBlank(int n) { + String s = ""; + for (int i = 0; i < n; i++) { + s = s + " "; + } + return s; + } + + private JPanel initFitOpsPane() { + double p = TableLayout.PREFERRED; + double[] rowSize = {p, p}; + double[] columnSize = {p, p, p, p, p}; + + ActionListener actionListener = getPreviewActionListener(); + + fontRadioGroup = new FontRadioGroup(); + fontFitRadio = new UIRadioButton(Toolkit.i18nText("Fine-Designer_Fit") + getFillBlank(4)); + fontFitRadio.setSelected(true); + fontNotFitRadio = new UIRadioButton(Toolkit.i18nText("Fine-Designer_Fit-No") + getFillBlank(2)); + addRadioToGroup(fontRadioGroup, fontFitRadio, fontNotFitRadio); + fontRadioGroup.addActionListener(actionListener); + + fitRadionGroup = new FitRadioGroup(); + horizonRadio = new UIRadioButton(FitType.HORIZONTAL_FIT.description()); + doubleRadio = new UIRadioButton(FitType.DOUBLE_FIT.description()); + notFitRadio = new UIRadioButton(FitType.NOT_FIT.description()); + addRadioToGroup(fitRadionGroup, doubleRadio, horizonRadio, notFitRadio); + fitRadionGroup.addActionListener(actionListener); + + + JPanel fitOpsPane = TableLayoutHelper.createTableLayoutPane(initFitComponents(), rowSize, columnSize); + fitOpsPane.setBorder(BorderFactory.createEmptyBorder(10, 13, 10, 10)); + return fitOpsPane; + } + + protected Component[][] initFitComponents() { + UILabel fitFontLabel = new UILabel(Toolkit.i18nText("Fine-Designer_Fit-Font")); + fitFontLabel.setHorizontalAlignment(SwingConstants.RIGHT); + UILabel reportFitLabel = new UILabel(Toolkit.i18nText("Fine-Designer_Fit_Report_Scale_Method")); + reportFitLabel.setHorizontalAlignment(SwingConstants.RIGHT); + Component[][] components = new Component[][]{ + new Component[]{fitFontLabel, createFontRadioGroupPane()}, + new Component[]{reportFitLabel, createFitAttrRadioGroupPane()} + }; + return components; + } + + private JPanel createFontRadioGroupPane() { + JPanel panel = FRGUIPaneFactory.createLeftFlowZeroGapBorderPane(); + panel.setLayout(new FlowLayout(FlowLayout.LEFT, 30, 0)); + panel.add(fontFitRadio); + panel.add(fontNotFitRadio); + return panel; + } + + private JPanel createFitAttrRadioGroupPane() { + JPanel panel = FRGUIPaneFactory.createLeftFlowZeroGapBorderPane(); + panel.setLayout(new FlowLayout(FlowLayout.LEFT, 30, 0)); + panel.add(doubleRadio); + panel.add(horizonRadio); + panel.add(notFitRadio); + return panel; + } + + private void addRadioToGroup(ButtonGroup buttonGroup, UIRadioButton... radios) { + for (UIRadioButton radio : radios) { + buttonGroup.add(radio); + } + } + + private JPanel initGlobalOpsPane(final ReportFitAttr globalFitAttr) { + final JPanel globalOpsPane = FRGUIPaneFactory.createRightFlowInnerContainer_S_Pane(); + globalCheck = new UICheckBox(Toolkit.i18nText("Fine-Designer_Fit-UseGlobal")); + globalOpsPane.add(globalCheck); + globalCheck.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + boolean isLocalConfig = !globalCheck.isSelected(); + //勾选全局时,采用全局保存的自适应属性更新界面 + if (!isLocalConfig) { + ReportFitAttr attr = globalFitAttr; + populateAttrPane(attr); + remove(BrowserFitAttrPane.this.borderPane); + initBorderPane(Toolkit.i18nText("Fine-Designer_Fit-Global")); + } else { + ReportFitAttr attr = localFitAttr; + populateAttrPane(attr); + remove(BrowserFitAttrPane.this.borderPane); + initBorderPane(Toolkit.i18nText("Fine-Designer_Fit-Local")); + } + fontRadioGroup.setEnabled(isLocalConfig); + fitRadionGroup.setEnabled(isLocalConfig); + editGlobalOps.setVisible(!isLocalConfig); + String fitOptions = getCurrentFitOptions(); + fitPreviewPane.refreshPreview(fitOptions, fitRadionGroup.isEnabled()); + } + }); + + editGlobalOps = new UIButton(Toolkit.i18nText("Fine-Designer_Fit-EditGlobal")); + editGlobalOps.setVisible(false); + editGlobalOps.addMouseListener(new MouseAdapter() { + public void mouseClicked(MouseEvent evt) { + fontRadioGroup.setEnabled(true); + fitRadionGroup.setEnabled(true); + String fitOptions = getCurrentFitOptions(); + + fitPreviewPane.refreshPreview(fitOptions, fitRadionGroup.isEnabled()); + } + + public void mouseEntered(MouseEvent e) { + setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR)); + } + + public void mouseExited(MouseEvent e) { + setCursor(Cursor.getDefaultCursor()); + } + }); + globalOpsPane.add(editGlobalOps); + return globalOpsPane; + } + + @Override + protected String title4PopupWindow() { + return Toolkit.i18nText("Fine-Designer_Fit-AttrSet"); + } + + @Override + public void populateBean(ReportFitAttr attr) { + if (attr == null) { + //如果为空, 就用全局的 + attr = this.jFormType.obtainFitAttr(); + populateGlobalComponents(); + } else { + initBorderPane(Toolkit.i18nText("Fine-Designer_Fit-Local")); + } + this.localFitAttr = attr; + populateAttrPane(attr); + } + + private void populateAttrPane(ReportFitAttr attr) { + fontRadioGroup.selectFontFit((attr).isFitFont()); + int fitState = FitStateCompatible.getNewTypeFromOld(attr.fitStateInPC()); + fitRadionGroup.selectIndexButton(fitState); + fitPreviewPane.refreshPreview(getCurrentFitOptions(), fitRadionGroup.isEnabled()); + } + + protected void populateGlobalComponents() { + globalCheck.setSelected(true); + fontRadioGroup.setEnabled(false); + fitRadionGroup.setEnabled(false); + editGlobalOps.setVisible(true); + initBorderPane(Toolkit.i18nText("Fine-Designer_Fit-Global")); + } + + //有八种组合, 不过有意义的就是6种, 以此为key去缓存里找对应的预览图片 + public String getCurrentFitOptions() { + return fitRadionGroup.getSelectRadioIndex() + "" + fontRadioGroup.getSelectRadioIndex(); + } + + private ActionListener getPreviewActionListener() { + return new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + String fitOptions = getCurrentFitOptions(); + fitPreviewPane.refreshPreview(fitOptions, fontRadioGroup.isEnabled()); + } + }; + } + + @Override + public ReportFitAttr updateBean() { + ReportFitAttr attr = new ReportFitAttr(); + attr.setFitFont(fontRadioGroup.isFontFit()); + int fitState = FitStateCompatible.getOldTypeFromNew(fitRadionGroup.getSelectRadioIndex()); + attr.setFitStateInPC(fitState); + + // 直接用全局的 + if (globalCheck.isSelected()) { + updateGlobalConfig(attr); + return null; + } + this.localFitAttr = attr; + return attr; + } + + private void updateGlobalConfig(final ReportFitAttr attr) { + + Configurations.update(new Worker() { + @Override + public void run() { + jFormType.updateFitAttr(attr); + } + + @Override + public Class[] targets() { + return new Class[]{FormFitConfig.class}; + } + }); + } +} diff --git a/designer-form/src/main/java/com/fr/design/fit/menupane/FitPreviewPane.java b/designer-form/src/main/java/com/fr/design/fit/menupane/FitPreviewPane.java new file mode 100644 index 000000000..ed387f39d --- /dev/null +++ b/designer-form/src/main/java/com/fr/design/fit/menupane/FitPreviewPane.java @@ -0,0 +1,76 @@ +package com.fr.design.fit.menupane; + +import com.fr.design.dialog.BasicPane; +import com.fr.design.gui.ilable.UILabel; +import com.fr.design.layout.FRGUIPaneFactory; +import com.fr.general.IOUtils; + +import javax.swing.ImageIcon; +import java.awt.BorderLayout; +import java.awt.Dimension; +import java.util.HashMap; +import java.util.Map; + +/** + * Created by Administrator on 2016/5/5/0005. + */ +public class FitPreviewPane extends BasicPane { + private static final String DEFAULT_FONT_TAG = "00"; + private static final String DEFAULT_TAG = "01"; + private static final String HORIZON_FONT_TAG = "10"; + private static final String HORIZON_TAG = "11"; + private static final String DOUBLE_FONT_TAG = "20"; + private static final String DOUBLE_TAG = "21"; + private static final String NOT_FONT_TAG = "30"; + private static final String NOT_TAG = "31"; + + private UILabel imageLabel; + private Map cachedPreviewImage = new HashMap(); + private Map globalCachedPreviewImage = new HashMap(); + + public FitPreviewPane() { + //初始化缓存图片, 有些无意义的组合. + initCacheImage(); + //初始化组件 + initComponents(); + this.setPreferredSize(new Dimension(540, 170)); + } + + //默认和不自适应时,字体自适应不起效,只有6张图 + private void initCacheImage() { + globalCachedPreviewImage.put(DEFAULT_FONT_TAG, new ImageIcon(IOUtils.readImage("/com/fr/design/images/reportfit/preview/gray/" + DOUBLE_FONT_TAG + ".png"))); + globalCachedPreviewImage.put(DEFAULT_TAG, new ImageIcon(IOUtils.readImage("/com/fr/design/images/reportfit/preview/gray/" + DOUBLE_TAG + ".png"))); + globalCachedPreviewImage.put(HORIZON_FONT_TAG, new ImageIcon(IOUtils.readImage("/com/fr/design/images/reportfit/preview/gray/" + HORIZON_FONT_TAG + ".png"))); + globalCachedPreviewImage.put(HORIZON_TAG, new ImageIcon(IOUtils.readImage("/com/fr/design/images/reportfit/preview/gray/" + HORIZON_TAG + ".png"))); + globalCachedPreviewImage.put(DOUBLE_FONT_TAG, new ImageIcon(IOUtils.readImage("/com/fr/design/images/reportfit/preview/gray/" + NOT_FONT_TAG + ".png"))); + globalCachedPreviewImage.put(DOUBLE_TAG, new ImageIcon(IOUtils.readImage("/com/fr/design/images/reportfit/preview/gray/" + NOT_FONT_TAG + ".png"))); + + cachedPreviewImage.put(DEFAULT_FONT_TAG, new ImageIcon(IOUtils.readImage("/com/fr/design/images/reportfit/preview/" + DOUBLE_FONT_TAG + ".png"))); + cachedPreviewImage.put(DEFAULT_TAG, new ImageIcon(IOUtils.readImage("/com/fr/design/images/reportfit/preview/" + DOUBLE_TAG + ".png"))); + cachedPreviewImage.put(HORIZON_FONT_TAG, new ImageIcon(IOUtils.readImage("/com/fr/design/images/reportfit/preview/" + HORIZON_FONT_TAG + ".png"))); + cachedPreviewImage.put(HORIZON_TAG, new ImageIcon(IOUtils.readImage("/com/fr/design/images/reportfit/preview/" + HORIZON_TAG + ".png"))); + cachedPreviewImage.put(DOUBLE_FONT_TAG, new ImageIcon(IOUtils.readImage("/com/fr/design/images/reportfit/preview/" + NOT_FONT_TAG + ".png"))); + cachedPreviewImage.put(DOUBLE_TAG, new ImageIcon(IOUtils.readImage("/com/fr/design/images/reportfit/preview/" + NOT_FONT_TAG + ".png"))); + + } + + private void initComponents() { + this.setLayout(FRGUIPaneFactory.createCenterFlowLayout()); + imageLabel = new UILabel(); + ImageIcon image = cachedPreviewImage.get(DEFAULT_TAG); + imageLabel.setIcon(image); + this.add(imageLabel, BorderLayout.CENTER); + } + + public void refreshPreview(String index, boolean isEditedable) { + ImageIcon newImageIcon = isEditedable ? cachedPreviewImage.get(index) : globalCachedPreviewImage.get(index); + imageLabel.setIcon(newImageIcon); + } + + @Override + protected String title4PopupWindow() { + return com.fr.design.i18n.Toolkit.i18nText("Fine-Designer_Fit-Preview"); + } + + +} diff --git a/designer-form/src/main/java/com/fr/design/fit/menupane/FitRadioGroup.java b/designer-form/src/main/java/com/fr/design/fit/menupane/FitRadioGroup.java new file mode 100644 index 000000000..46fb32768 --- /dev/null +++ b/designer-form/src/main/java/com/fr/design/fit/menupane/FitRadioGroup.java @@ -0,0 +1,80 @@ +package com.fr.design.fit.menupane; + +import com.fr.design.gui.ibutton.UIRadioButton; + +import javax.swing.AbstractButton; +import javax.swing.ButtonGroup; +import java.awt.event.ActionListener; +import java.util.ArrayList; +import java.util.List; + +/** + * 自适应四个按钮选项的group + *

+ * Created by Administrator on 2016/5/5/0005. + */ +public class FitRadioGroup extends ButtonGroup { + + private List radioButtons = new ArrayList(); + + @Override + public void add(AbstractButton button) { + super.add(button); + + UIRadioButton radioButton = (UIRadioButton) button; + radioButtons.add(radioButton); + } + + /** + * 设置按钮状态 + */ + public boolean isEnabled() { + return radioButtons.get(0).isEnabled(); + } + + /** + * 设置按钮状态 + */ + public void setEnabled(boolean enabled) { + for (UIRadioButton radioButton : radioButtons) { + radioButton.setEnabled(enabled); + } + } + + /** + * 获取当前选中的按钮index + * + * @return 按钮index + */ + public int getSelectRadioIndex() { + for (int i = 0, len = radioButtons.size(); i < len; i++) { + if (radioButtons.get(i).isSelected()) { + return i; + } + } + + return 0; + } + + /** + * 选中指定index的按钮 + */ + public void selectIndexButton(int index) { + if (index < 0 || index > radioButtons.size() - 1) { + return; + } + + UIRadioButton button = radioButtons.get(index); + button.setSelected(true); + } + + /** + * 给所有的按钮加上监听 + */ + public void addActionListener(ActionListener actionListener) { + for (UIRadioButton radioButton : radioButtons) { + radioButton.addActionListener(actionListener); + } + } + +} diff --git a/designer-form/src/main/java/com/fr/design/fit/menupane/FontRadioGroup.java b/designer-form/src/main/java/com/fr/design/fit/menupane/FontRadioGroup.java new file mode 100644 index 000000000..8d26f4074 --- /dev/null +++ b/designer-form/src/main/java/com/fr/design/fit/menupane/FontRadioGroup.java @@ -0,0 +1,17 @@ +package com.fr.design.fit.menupane; + +/** + * 字体的两个选项组成的group + *

+ * Created by Administrator on 2016/5/5/0005. + */ +public class FontRadioGroup extends FitRadioGroup { + + public void selectFontFit(boolean isFontFit) { + selectIndexButton(isFontFit ? 0 : 1); + } + + public boolean isFontFit() { + return getSelectRadioIndex() == 0; + } +} diff --git a/designer-form/src/main/java/com/fr/design/fit/menupane/FormFitAttrPane.java b/designer-form/src/main/java/com/fr/design/fit/menupane/FormFitAttrPane.java new file mode 100644 index 000000000..be1411264 --- /dev/null +++ b/designer-form/src/main/java/com/fr/design/fit/menupane/FormFitAttrPane.java @@ -0,0 +1,58 @@ +package com.fr.design.fit.menupane; + +import com.fr.design.beans.BasicBeanPane; +import com.fr.design.fit.NewJForm; +import com.fr.report.fit.ReportFitAttr; + +import javax.swing.BorderFactory; +import javax.swing.BoxLayout; + +/** + * Created by Administrator on 2015/7/6 0006. + */ +public class FormFitAttrPane extends BasicBeanPane { + + private BrowserFitAttrPane attrPane; + private NewJForm newJForm; + + public FormFitAttrPane(NewJForm newJForm) { + this.newJForm = newJForm; + initComponents(); + } + + private void initComponents() { + this.setLayout(new BoxLayout(this, BoxLayout.Y_AXIS)); + this.setBorder(BorderFactory.createEmptyBorder(10, 10, 2, 10)); + attrPane = new BrowserFitAttrPane(this.newJForm.getJFormType(),this.newJForm.getTarget().getReportFitAttr()); + this.add(attrPane); + + } + + /** + * 展示界面 + * + * @param fitAttr 自适应属性 + */ + public void populateBean(ReportFitAttr fitAttr) { + attrPane.populateBean(fitAttr); + } + + /** + * 提交数据 + * + * @return 界面上的更新数据 + */ + public ReportFitAttr updateBean() { + return attrPane.updateBean(); + } + + /** + * 标题 + * + * @return 标题 + */ + protected String title4PopupWindow() { + return com.fr.design.i18n.Toolkit.i18nText("Fine-Designer_Fit_PC_Adaptive_Attr"); + } + +} diff --git a/designer-form/src/main/java/com/fr/design/fit/toolbar/SwitchAction.java b/designer-form/src/main/java/com/fr/design/fit/toolbar/SwitchAction.java new file mode 100644 index 000000000..e14d39a68 --- /dev/null +++ b/designer-form/src/main/java/com/fr/design/fit/toolbar/SwitchAction.java @@ -0,0 +1,287 @@ +package com.fr.design.fit.toolbar; + +import com.fr.design.actions.UpdateAction; +import com.fr.design.dialog.FineJOptionPane; +import com.fr.design.file.TemplateTreePane; +import com.fr.design.fit.DesignerUIModeConfig; +import com.fr.design.fit.NewJForm; +import com.fr.design.fit.common.AdaptiveSwitchUtil; +import com.fr.design.fit.common.TemplateTool; +import com.fr.design.gui.ibutton.UIButton; +import com.fr.design.i18n.Toolkit; +import com.fr.design.mainframe.BaseJForm; +import com.fr.design.mainframe.DesignerContext; +import com.fr.design.mainframe.JTemplate; +import com.fr.design.menu.MenuKeySet; +import com.fr.file.FILE; +import com.fr.file.FILEChooserPane; +import com.fr.file.FileFILE; +import com.fr.file.FileNodeFILE; +import com.fr.file.MemFILE; +import com.fr.file.filetree.FileNode; +import com.fr.general.IOUtils; +import com.fr.log.FineLoggerFactory; + +import javax.swing.Icon; +import javax.swing.ImageIcon; +import javax.swing.JComponent; +import javax.swing.JLabel; +import javax.swing.JOptionPane; +import javax.swing.JPanel; +import javax.swing.KeyStroke; +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.Font; +import java.awt.event.ActionEvent; +import java.nio.file.Paths; + +import static javax.swing.JOptionPane.OK_CANCEL_OPTION; +import static javax.swing.JOptionPane.WARNING_MESSAGE; +import static javax.swing.JOptionPane.YES_NO_OPTION; + +/** + * Created by kerry on 2020-06-02 + */ +public class SwitchAction extends UpdateAction { + + private UIButton switchBtn; + private static final Icon SWITCH_ICON = IOUtils.readIcon("/com/fr/design/form/images/icon_switch.png"); + private static final String LOADING_ICON_PATH = "/com/fr/design/form/images/loading.png"; + private static final MenuKeySet SWITCH_ATTR = new MenuKeySet() { + @Override + public char getMnemonic() { + return 'C'; + } + + @Override + public String getMenuName() { + return Toolkit.i18nText("Fine-Designer_Fit_Switch_To_Old_UI"); + } + + @Override + public KeyStroke getKeyStroke() { + return null; + } + }; + + public SwitchAction() { + initMenuStyle(); + } + + private void initMenuStyle() { + this.setMenuKeySet(SWITCH_ATTR); + this.setName(getMenuKeySet().getMenuKeySetName()); + this.setMnemonic(getMenuKeySet().getMnemonic()); + this.setSmallIcon(SWITCH_ICON); + this.setAccelerator(getMenuKeySet().getKeyStroke()); + switchBtn = (UIButton) this.createToolBarComponent(); + switchBtn.set4ToolbarButton(); + switchBtn.setIcon(SWITCH_ICON); + Font oldFont = switchBtn.getFont(); + switchBtn.setFont(new Font(oldFont.getName(),oldFont.getStyle(),12)); + switchBtn.setForeground(new Color(0x33, 0x33, 0x34)); + freshSwitchButton(); + } + + @Override + public void actionPerformed(ActionEvent e) { + //如果此时模板还在切换中,则直接返回 + if (AdaptiveSwitchUtil.isSwitchJFromIng() || !saveCurrentEditingTemplate()) + return; + backToFormTab(); + if (confirmSwitchDialog() && backUpOldModeJTemplate()) { + AdaptiveSwitchUtil.setSwitchJFromIng(1); + showLoadingJPanel(); + if (DesignerUIModeConfig.getInstance().newUIMode()) { + AdaptiveSwitchUtil.switch2OldUI(); + } else { + AdaptiveSwitchUtil.switch2NewUI(); + } + } + } + + /** + * @Description: 对未存储在磁盘的模板进行界面切换 + * @return: + * @Author: Henry.Wang + * @date: 2020/10/22 17:26 + */ + public boolean saveCurrentEditingTemplate() { + JTemplate jTemplate = TemplateTool.getCurrentEditingTemplate(); + if (!(jTemplate.getEditingFILE() instanceof FileNodeFILE) || !jTemplate.getEditingFILE().exists()) { + int returnVal = FineJOptionPane.showConfirmDialog( + DesignerContext.getDesignerFrame(), + Toolkit.i18nText("Fine-Design_Basic_Web_Preview_Message"), + Toolkit.i18nText("Fine-Design_Basic_Tool_Tips"), + OK_CANCEL_OPTION, + WARNING_MESSAGE); + if (returnVal == JOptionPane.YES_OPTION) { + jTemplate.stopEditing(); + if ((jTemplate.getEditingFILE() instanceof MemFILE) || !jTemplate.getEditingFILE().exists()) { + return jTemplate.saveTemplate(); + } else if (jTemplate.getEditingFILE() instanceof FileFILE) { + return jTemplate.saveAsTemplate2Env(); + } + } + return false; + } + return true; + } + + /** + * @Description: 显示加载中界面 + * @param + * @return: + * @Author: Henry.Wang + * @date: 2020/9/30 15:18 + */ + public void showLoadingJPanel() { + JComponent area = TemplateTool.getCurrentEditingNewJForm().getFormDesign().getArea(); + JComponent formDesigner = TemplateTool.getCurrentEditingNewJForm().getFormDesign(); + area.remove(formDesigner); + + JPanel loadingJPanel = new JPanel(new BorderLayout()); + loadingJPanel.setBackground(Color.WHITE); + loadingJPanel.setBounds(formDesigner.getBounds()); + JLabel jLabel = new JLabel(new ImageIcon(this.getClass().getResource(LOADING_ICON_PATH)), JLabel.CENTER); + loadingJPanel.add(jLabel, BorderLayout.CENTER); + + area.setLayout(null); + area.add(loadingJPanel); + DesignerContext.getDesignerFrame().setVisible(true); + } + + + /** + * @Description: 如果是在报表块编辑状态进行切换时,会先回到表单页 + * @param + * @return: + * @Author: Henry.Wang + * @date: 2020/9/30 15:20 + */ + private void backToFormTab() { + NewJForm newJForm = TemplateTool.getCurrentEditingNewJForm(); + if (newJForm != null && newJForm.getEditingReportIndex() != BaseJForm.FORM_TAB) { + newJForm.tabChanged(BaseJForm.FORM_TAB); + } + } + + /** + * @param + * @Description: 备份老设计模式下的模板文件 + * @return: + * @Author: Henry.Wang + * @date: 2020/9/23 16:24 + */ + private boolean backUpOldModeJTemplate() { + JTemplate jTemplate = TemplateTool.getCurrentEditingTemplate(); + if (jTemplate != null && !DesignerUIModeConfig.getInstance().newUIMode()) { + FILE editingFILE = jTemplate.getEditingFILE(); + if (editingFILE != null && editingFILE.exists()) { + try { + //待备份的文件名称 + String fileName = editingFILE.getName().substring(0, editingFILE.getName().length() - jTemplate.suffix().length()) + "_bak"; + //生成要备份的文件对象 + FILE backUpFile = null; + FileNodeFILE directory = getFileParentDirectory(jTemplate.getEditingFILE()); + if (directory != null) + backUpFile = new FileNodeFILE(directory, fileName + jTemplate.suffix(), false); + //要备份的文件存在 + if (backUpFile != null && backUpFile.exists()) { + if (!confirmCoverFileDialog()) { + FILEChooserPane fileChooser = FILEChooserPane.getInstance(true, true); + fileChooser.setFileNameTextField(fileName, jTemplate.suffix()); + fileChooser.setCurrentDirectory(directory); + int chooseResult = fileChooser.showSaveDialog(DesignerContext.getDesignerFrame(), jTemplate.suffix()); + if ((chooseResult == FILEChooserPane.CANCEL_OPTION) || + (chooseResult == FILEChooserPane.JOPTIONPANE_CANCEL_OPTION)) { + return false; + } + backUpFile = fileChooser.getSelectedFILE(); + } + } + jTemplate.getTarget().export(backUpFile.asOutputStream()); + TemplateTreePane.getInstance().refresh(); + } catch (Exception e) { + FineLoggerFactory.getLogger().error(e.getMessage()); + return false; + } + return true; + } + } + return true; + } + + /** + * @param file + * @Description: 获得当前文件的父目录 + * @return: + * @Author: Henry.Wang + * @date: 2020/9/23 16:50 + */ + public static FileNodeFILE getFileParentDirectory(FILE file) { + FileNodeFILE fileParent = null; + if (file instanceof FileNodeFILE) { + FileNodeFILE fileNodeFILE = (FileNodeFILE) file; + FileNode fileNode = new FileNode(Paths.get(fileNodeFILE.getPath()).getParent().toString(), true); + fileParent = new FileNodeFILE(fileNode, fileNodeFILE.getEnvPath()); + } + return fileParent; + } + + /** + * @param + * @Description: 确认是否弹出框 + * @return: 点击确定返回true + * @Author: Henry.Wang + * @date: 2020/9/18 11:20 + */ + private boolean confirmSwitchDialog() { + Object message = DesignerUIModeConfig.getInstance().newUIMode() ? Toolkit.i18nText("Fine-Designer_Fit_Confirm_New_Old_Switch") : Toolkit.i18nText("Fine-Designer_Fit_Confirm_Old_New_Switch"); + int returnVal = FineJOptionPane.showConfirmDialog( + DesignerContext.getDesignerFrame(), + message, + Toolkit.i18nText("Fine-Design_Basic_Confirm"), + YES_NO_OPTION); + return returnVal == JOptionPane.YES_OPTION; + } + + /** + * @param + * @Description: 是否覆盖文件 + * @return: + * @Author: Henry.Wang + * @date: 2020/9/23 17:00 + */ + private boolean confirmCoverFileDialog() { + int returnVal = FineJOptionPane.showConfirmDialog( + DesignerContext.getDesignerFrame(), + Toolkit.i18nText("Fine-Designer_Fit_Cover_File_Switch"), + Toolkit.i18nText("Fine-Design_Basic_Confirm"), + YES_NO_OPTION); + return returnVal == JOptionPane.YES_OPTION; + } + + /** + * @param + * @Description:更新按钮的状态 + * @return: + * @Author: Henry.Wang + * @date: 2020/8/31 16:39 + */ + public UIButton freshSwitchButton() { + if (DesignerUIModeConfig.getInstance().newUIMode()) { + switchBtn.setToolTipText(Toolkit.i18nText("Fine-Designer_Fit_Switch_To_Old_UI")); + switchBtn.setText(Toolkit.i18nText("Fine-Designer_Fit_Switch_To_Old_Version")); + } else { + switchBtn.setToolTipText(Toolkit.i18nText("Fine-Designer_Fit_Switch_To_New_UI")); + switchBtn.setText(Toolkit.i18nText("Fine-Designer_Fit_Switch_To_New_Version")); + } + return switchBtn; + } + + public UIButton getToolBarButton() { + return freshSwitchButton(); + } + +} diff --git a/designer-form/src/main/java/com/fr/design/mainframe/CoverReportPane.java b/designer-form/src/main/java/com/fr/design/mainframe/CoverReportPane.java index d976691c6..e475133a9 100644 --- a/designer-form/src/main/java/com/fr/design/mainframe/CoverReportPane.java +++ b/designer-form/src/main/java/com/fr/design/mainframe/CoverReportPane.java @@ -1,10 +1,10 @@ package com.fr.design.mainframe; import com.fr.design.constants.UIConstants; +import com.fr.design.fit.DesignerUIModeConfig; import com.fr.design.gui.ibutton.UIButton; import com.fr.design.icon.IconPathConstants; import com.fr.share.ShareConstants; -import com.fr.general.FRScreen; import com.fr.general.IOUtils; import com.fr.stable.StringUtils; @@ -63,7 +63,7 @@ public class CoverReportPane extends CoverPane implements HelpDialogHandler{ // controlMode = IOUtils.readIcon(IconPathConstants.TD_EL_SHARE_CLOSE_ICON_PATH); controlButton.setVisible(false); helpDialog = new WidgetHelpDialog(DesignerContext.getDesignerFrame(), helpMsg); - double screenValue = FRScreen.getDesignScreenByDimension(Toolkit.getDefaultToolkit().getScreenSize()).getValue(); + double screenValue = DesignerUIModeConfig.getInstance().getDesignScreenByDimension(Toolkit.getDefaultToolkit().getScreenSize()).getValue(); int offsetX = 0; if (screenValue < FormArea.DEFAULT_SLIDER) { offsetX = (int) ((1 - screenValue / FormArea.DEFAULT_SLIDER) diff --git a/designer-form/src/main/java/com/fr/design/mainframe/EditingMouseListener.java b/designer-form/src/main/java/com/fr/design/mainframe/EditingMouseListener.java index 524dd8bf1..b9ce52fc5 100644 --- a/designer-form/src/main/java/com/fr/design/mainframe/EditingMouseListener.java +++ b/designer-form/src/main/java/com/fr/design/mainframe/EditingMouseListener.java @@ -194,7 +194,7 @@ public class EditingMouseListener extends MouseInputAdapter { e.translatePoint(oldX - e.getX(), oldY - e.getY()); } - private void offsetEventPoint(MouseEvent e){ + private void offsetEventPoint(MouseEvent e) { int x = designer.getRelativeX(e.getX()); int y = designer.getRelativeY(e.getY()); e.translatePoint(x - e.getX(), y - e.getY()); @@ -206,8 +206,8 @@ public class EditingMouseListener extends MouseInputAdapter { * @param e 鼠标事件 */ public void mouseReleased(MouseEvent e) { - MouseEvent transEvent = new MouseEvent(e.getComponent(), MouseEvent.MOUSE_CLICKED, e.getWhen(), e.getModifiers(), e.getX(), e.getY(), e.getXOnScreen(), e.getYOnScreen(), e.getClickCount(), e.isPopupTrigger(), e.getButton()); - MouseEvent clickEvent = new MouseEvent(e.getComponent(), MouseEvent.MOUSE_CLICKED, e.getWhen(), e.getModifiers(), e.getX(), e.getY(), e.getXOnScreen(), e.getYOnScreen(), e.getClickCount(), e.isPopupTrigger(), e.getButton()); + MouseEvent transEvent = new MouseEvent(e.getComponent(), MouseEvent.MOUSE_CLICKED, e.getWhen(), e.getModifiers(), e.getX(), e.getY(), e.getXOnScreen(), e.getYOnScreen(), e.getClickCount(), e.isPopupTrigger(), e.getButton()); + MouseEvent clickEvent = new MouseEvent(e.getComponent(), MouseEvent.MOUSE_CLICKED, e.getWhen(), e.getModifiers(), e.getX(), e.getY(), e.getXOnScreen(), e.getYOnScreen(), e.getClickCount(), e.isPopupTrigger(), e.getButton()); int oldX = e.getX(); int oldY = e.getY(); offsetEventPoint(e); @@ -240,7 +240,7 @@ public class EditingMouseListener extends MouseInputAdapter { } } - private boolean isAutoFire(MouseEvent transEvent, MouseEvent clickEvent ) { + private boolean isAutoFire(MouseEvent transEvent, MouseEvent clickEvent) { offsetEventPoint(transEvent); XCreator xCreator = designer.getComponentAt(transEvent); return (pressX != clickEvent.getX() || pressY != clickEvent.getY()) @@ -728,15 +728,15 @@ public class EditingMouseListener extends MouseInputAdapter { /** * 刷新顶层组件 - * */ - public void refreshTopXCreator(boolean isEditing){ + */ + public void refreshTopXCreator(boolean isEditing) { designer.refreshTopXCreator(isEditing); } /** * 刷新顶层组件 - * */ - public void refreshTopXCreator(){ + */ + public void refreshTopXCreator() { refreshTopXCreator(false); } diff --git a/designer-form/src/main/java/com/fr/design/mainframe/FormArea.java b/designer-form/src/main/java/com/fr/design/mainframe/FormArea.java index f127f8ea0..bc4e941fc 100644 --- a/designer-form/src/main/java/com/fr/design/mainframe/FormArea.java +++ b/designer-form/src/main/java/com/fr/design/mainframe/FormArea.java @@ -1,14 +1,13 @@ package com.fr.design.mainframe; -import com.fr.base.ScreenResolution; import com.fr.common.inputevent.InputEventBaseOnOS; -import com.fr.design.designer.beans.events.DesignerEditListener; import com.fr.design.designer.beans.events.DesignerEvent; import com.fr.design.designer.creator.XCreator; import com.fr.design.designer.creator.XLayoutContainer; import com.fr.design.designer.creator.XWBorderLayout; import com.fr.design.designer.creator.XWFitLayout; -import com.fr.design.file.HistoryTemplateListPane; +import com.fr.design.fit.DesignerUIModeConfig; +import com.fr.form.fit.NewFormMarkAttr; import com.fr.design.gui.ilable.UILabel; import com.fr.design.gui.itextfield.UINumberField; import com.fr.design.layout.TableLayout; @@ -79,7 +78,6 @@ public class FormArea extends JComponent implements ScrollRulerComponent { private boolean isValid = true; // 初始时滑块值为100,托动后的值设为START_VALUE; private double START_VALUE = DEFAULT_SLIDER; - private int resolution = ScreenResolution.getScreenResolution(); private double screenValue; @@ -253,7 +251,7 @@ public class FormArea extends JComponent implements ScrollRulerComponent { private void initCalculateSize() { Toolkit toolkit = Toolkit.getDefaultToolkit(); Dimension scrnsize = toolkit.getScreenSize(); - this.screenValue = FRScreen.getDesignScreenByDimension(scrnsize).getValue(); + this.screenValue = DesignerUIModeConfig.getInstance().getDesignScreenByDimension(scrnsize).getValue(); XLayoutContainer root = FormArea.this.designer.getRootComponent(); // 7.1.1不放缩放滑块,但表单大小仍按屏幕分辨率调整 // slidePane.populateBean(screenValue); @@ -285,10 +283,19 @@ public class FormArea extends JComponent implements ScrollRulerComponent { // 失去焦点时,可以认为输入结束 int width = (int) ((UINumberField) e.getSource()).getValue(); changeWidthPaneValue(width); + changeFormMarkAttrWidth(width); } }); } + private void changeFormMarkAttrWidth(int width) { + NewFormMarkAttr newFormMarkAttr = designer.getTarget().getAttrMark(NewFormMarkAttr.XML_TAG); + if (newFormMarkAttr != null) { + newFormMarkAttr.setBodyWidth(width); + } + } + + private void changeWidthPaneValue(int width) { XWFitLayout layout = (XWFitLayout) designer.getRootComponent(); if (width != layout.toData().getContainerWidth()) { @@ -303,6 +310,7 @@ public class FormArea extends JComponent implements ScrollRulerComponent { public void actionPerformed(ActionEvent evt) { int height = (int) ((UINumberField) evt.getSource()).getValue(); changeHeightPaneValue(height); + changeFormMarkAttrHeight(height); } }); heightPane.addFocusListener( @@ -315,6 +323,14 @@ public class FormArea extends JComponent implements ScrollRulerComponent { }); } + private void changeFormMarkAttrHeight(int height){ + NewFormMarkAttr newFormMarkAttr = designer.getTarget().getAttrMark(NewFormMarkAttr.XML_TAG); + if (newFormMarkAttr != null) { + newFormMarkAttr.setBodyHeight(height); + } + } + + private void changeHeightPaneValue(int height) { XWFitLayout layout = (XWFitLayout) designer.getRootComponent(); if (height != layout.toData().getContainerHeight()) { @@ -383,7 +399,7 @@ public class FormArea extends JComponent implements ScrollRulerComponent { /** * 修改大小后,再根据屏幕分辨率调整下 */ - private void doReCalculateRoot(int width, int height, XWFitLayout layout) { + public void doReCalculateRoot(int width, int height, XWFitLayout layout) { // double value = slidePane.updateBean(); //重置滑块的值为默认值100 START_VALUE = DEFAULT_SLIDER; diff --git a/designer-form/src/main/java/com/fr/design/mainframe/FormDesigner.java b/designer-form/src/main/java/com/fr/design/mainframe/FormDesigner.java index 1d4fad8e3..f167a2c72 100644 --- a/designer-form/src/main/java/com/fr/design/mainframe/FormDesigner.java +++ b/designer-form/src/main/java/com/fr/design/mainframe/FormDesigner.java @@ -43,6 +43,7 @@ import com.fr.design.designer.properties.FormWidgetAuthorityEditPane; import com.fr.design.dialog.FineJOptionPane; import com.fr.design.event.DesignerOpenedListener; import com.fr.design.file.HistoryTemplateListPane; +import com.fr.design.fit.DesignerUIModeConfig; import com.fr.design.form.util.XCreatorConstants; import com.fr.design.fun.RightSelectionHandlerProvider; import com.fr.design.mainframe.toolbar.ToolBarMenuDockPlus; @@ -144,7 +145,7 @@ public class FormDesigner extends TargetComponent

implements TreeSelection // 存储被选择组件和剪切板的model private transient SelectionModel selectionModel; - private int resolution = ScreenResolution.getScreenResolution(); + private int resolution = DesignerUIModeConfig.getInstance().getScreenResolution(); // 编辑状态的事件表 private CreatorEventListenerTable edit; protected List designerActions; diff --git a/designer-form/src/main/java/com/fr/design/mainframe/FormModelAdapter.java b/designer-form/src/main/java/com/fr/design/mainframe/FormModelAdapter.java index e5617c946..efc7ff2b0 100644 --- a/designer-form/src/main/java/com/fr/design/mainframe/FormModelAdapter.java +++ b/designer-form/src/main/java/com/fr/design/mainframe/FormModelAdapter.java @@ -11,15 +11,11 @@ import com.fr.form.ui.BaseChartEditor; import com.fr.form.ui.ElementCaseEditor; import com.fr.form.ui.Widget; import com.fr.script.Calculator; -import com.fr.stable.EmbParaFilter; import com.fr.stable.ParameterProvider; import com.fr.stable.js.WidgetName; -import com.fr.util.ParameterApplyHelper; import java.util.ArrayList; -import java.util.HashMap; import java.util.List; -import java.util.Map; public class FormModelAdapter extends DesignModelAdapter> { @@ -46,16 +42,6 @@ public class FormModelAdapter extends DesignModelAdapter> jTemplate.populateParameter(); } - @Override - public void tableDataParametersChanged(String oldName, String tdName, TableData tableData) { - Map map = new HashMap<>(); - addGlobalParameters(map); - updateTableDataParameters(oldName, tdName, tableData, map, parameterProvider -> !EmbParaFilter.isFRLayerTypePara(parameterProvider)); - ParameterApplyHelper.addPara2Map(map, this.getBook().getTemplateParameters()); - parameters = map.values().toArray(new Parameter[0]); - jTemplate.populateParameter(); - } - /** * 控件配置改变. */ @@ -149,13 +135,6 @@ public class FormModelAdapter extends DesignModelAdapter> @Override protected Parameter[] getLatestParameters() { - Map map = new HashMap<>(); - addGlobalParameters(map); - - addTableDataParameters(map, parameterProvider -> !EmbParaFilter.isFRLayerTypePara(parameterProvider)); - - ParameterApplyHelper.addPara2Map(map, this.getBook().getTemplateParameters()); - - return map.values().toArray(new Parameter[0]); + return this.getBook().getParameters(); } } diff --git a/designer-form/src/main/java/com/fr/design/preview/DeveloperPreview.java b/designer-form/src/main/java/com/fr/design/preview/DeveloperPreview.java new file mode 100644 index 000000000..7db99129c --- /dev/null +++ b/designer-form/src/main/java/com/fr/design/preview/DeveloperPreview.java @@ -0,0 +1,53 @@ +package com.fr.design.preview; + +import com.fr.design.file.HistoryTemplateListCache; +import com.fr.design.fun.impl.AbstractPreviewProvider; +import com.fr.design.mainframe.JForm; +import com.fr.design.mainframe.JTemplate; +import com.fr.general.web.ParameterConstants; + +import java.util.HashMap; +import java.util.Map; + +/** + * Created by kerry on 2020-04-23 + */ +public class DeveloperPreview extends AbstractPreviewProvider { + private static final int PREVIEW_TYPE = 7; + + @Override + public String nameForPopupItem() { + return com.fr.design.i18n.Toolkit.i18nText("Fine-Designer_Fit_Developer_Preview"); + } + + @Override + public String iconPathForPopupItem() { + return "com/fr/design/form/images/developer_preview.png"; + } + + @Override + public String iconPathForLarge() { + return "com/fr/design/form/images/developer_preview24.png"; + } + + @Override + public int previewTypeCode() { + return PREVIEW_TYPE; + } + + + @Override + public Map parametersForPreview() { + Map map = new HashMap(); + map.put(ParameterConstants.OP, "developer_preview"); + return map; + } + + @Override + public boolean accept(JTemplate jTemplate) { + if (jTemplate == null) { + jTemplate = HistoryTemplateListCache.getInstance().getCurrentEditingTemplate(); + } + return jTemplate instanceof JForm; + } +} diff --git a/designer-form/src/main/java/com/fr/design/preview/FormAdaptivePreview.java b/designer-form/src/main/java/com/fr/design/preview/FormAdaptivePreview.java new file mode 100644 index 000000000..01bd0e126 --- /dev/null +++ b/designer-form/src/main/java/com/fr/design/preview/FormAdaptivePreview.java @@ -0,0 +1,54 @@ +package com.fr.design.preview; + +import com.fr.design.file.HistoryTemplateListCache; +import com.fr.design.fun.impl.AbstractPreviewProvider; +import com.fr.design.mainframe.JForm; +import com.fr.design.mainframe.JTemplate; +import com.fr.general.web.ParameterConstants; + +import java.util.HashMap; +import java.util.Map; + +/** + * Created by kerry on 2020-04-23 + */ +public class FormAdaptivePreview extends AbstractPreviewProvider { + private static final int PREVIEW_TYPE = 6; + public static final String PREVIEW_OP = "form_adaptive"; + + @Override + public String nameForPopupItem() { + return com.fr.design.i18n.Toolkit.i18nText("Fine-Designer_Fit_New_Form_Preview"); + } + + @Override + public String iconPathForPopupItem() { + return "com/fr/design/images/buttonicon/runs.png"; + } + + @Override + public String iconPathForLarge() { + return "com/fr/design/images/buttonicon/run24.png"; + } + + @Override + public int previewTypeCode() { + return PREVIEW_TYPE; + } + + + @Override + public Map parametersForPreview() { + Map map = new HashMap(); + map.put(ParameterConstants.OP, PREVIEW_OP); + return map; + } + + @Override + public boolean accept(JTemplate jTemplate) { + if (jTemplate == null) { + jTemplate = HistoryTemplateListCache.getInstance().getCurrentEditingTemplate(); + } + return jTemplate instanceof JForm; + } +} diff --git a/designer-form/src/main/resources/com/fr/design/form/images/developer_preview.png b/designer-form/src/main/resources/com/fr/design/form/images/developer_preview.png new file mode 100755 index 000000000..7d7e064d1 Binary files /dev/null and b/designer-form/src/main/resources/com/fr/design/form/images/developer_preview.png differ diff --git a/designer-form/src/main/resources/com/fr/design/form/images/developer_preview24.png b/designer-form/src/main/resources/com/fr/design/form/images/developer_preview24.png new file mode 100755 index 000000000..fc3d3d347 Binary files /dev/null and b/designer-form/src/main/resources/com/fr/design/form/images/developer_preview24.png differ diff --git a/designer-form/src/main/resources/com/fr/design/form/images/icon_form_tab.png b/designer-form/src/main/resources/com/fr/design/form/images/icon_form_tab.png new file mode 100755 index 000000000..a6ad9f86c Binary files /dev/null and b/designer-form/src/main/resources/com/fr/design/form/images/icon_form_tab.png differ diff --git a/designer-form/src/main/resources/com/fr/design/form/images/icon_switch.png b/designer-form/src/main/resources/com/fr/design/form/images/icon_switch.png new file mode 100644 index 000000000..3730b3b79 Binary files /dev/null and b/designer-form/src/main/resources/com/fr/design/form/images/icon_switch.png differ diff --git a/designer-form/src/main/resources/com/fr/design/form/images/icon_switch_disable.png b/designer-form/src/main/resources/com/fr/design/form/images/icon_switch_disable.png new file mode 100755 index 000000000..daee74b51 Binary files /dev/null and b/designer-form/src/main/resources/com/fr/design/form/images/icon_switch_disable.png differ diff --git a/designer-form/src/main/resources/com/fr/design/form/images/icon_switch_new_ui.png b/designer-form/src/main/resources/com/fr/design/form/images/icon_switch_new_ui.png new file mode 100755 index 000000000..f7a2a86f6 Binary files /dev/null and b/designer-form/src/main/resources/com/fr/design/form/images/icon_switch_new_ui.png differ diff --git a/designer-form/src/main/resources/com/fr/design/form/images/icon_switch_old_ui.png b/designer-form/src/main/resources/com/fr/design/form/images/icon_switch_old_ui.png new file mode 100755 index 000000000..08217d411 Binary files /dev/null and b/designer-form/src/main/resources/com/fr/design/form/images/icon_switch_old_ui.png differ diff --git a/designer-form/src/main/resources/com/fr/design/form/images/loading.png b/designer-form/src/main/resources/com/fr/design/form/images/loading.png new file mode 100644 index 000000000..d479fb376 Binary files /dev/null and b/designer-form/src/main/resources/com/fr/design/form/images/loading.png differ diff --git a/designer-form/src/test/java/com/fr/design/fit/FitStateCompatibleTest.java b/designer-form/src/test/java/com/fr/design/fit/FitStateCompatibleTest.java new file mode 100644 index 000000000..a826cb4b2 --- /dev/null +++ b/designer-form/src/test/java/com/fr/design/fit/FitStateCompatibleTest.java @@ -0,0 +1,24 @@ +package com.fr.design.fit; + +import org.junit.Assert; +import org.junit.Test; + +/** + * Created by kerry on 2020-04-27 + */ +public class FitStateCompatibleTest { + @Test + public void testGetNewTypeFromOld() { + Assert.assertEquals(0, FitStateCompatible.getNewTypeFromOld(0)); + Assert.assertEquals(1, FitStateCompatible.getNewTypeFromOld(1)); + Assert.assertEquals(0, FitStateCompatible.getNewTypeFromOld(2)); + Assert.assertEquals(2, FitStateCompatible.getNewTypeFromOld(3)); + } + + @Test + public void testGetOldTypeFromNew() { + Assert.assertEquals(2, FitStateCompatible.getOldTypeFromNew(0)); + Assert.assertEquals(1, FitStateCompatible.getOldTypeFromNew(1)); + Assert.assertEquals(3, FitStateCompatible.getOldTypeFromNew(2)); + } +} diff --git a/designer-form/src/test/java/com/fr/design/fit/FormUIModeConfigTest.java b/designer-form/src/test/java/com/fr/design/fit/FormUIModeConfigTest.java new file mode 100644 index 000000000..12c57280c --- /dev/null +++ b/designer-form/src/test/java/com/fr/design/fit/FormUIModeConfigTest.java @@ -0,0 +1,22 @@ +package com.fr.design.fit; + +import org.junit.Assert; +import org.junit.Test; + +/** + * Created by kerry on 2020-06-05 + */ +public class FormUIModeConfigTest { + @Test + public void testSetNewUIMode(){ + DesignerUIModeConfig.getInstance().setNewUIMode(); + Assert.assertTrue(DesignerUIModeConfig.getInstance().newUIMode()); + } + + @Test + public void testSetOldUIMode(){ + Assert.assertTrue(DesignerUIModeConfig.getInstance().newUIMode()); + DesignerUIModeConfig.getInstance().setOldUIMode(); + Assert.assertFalse(DesignerUIModeConfig.getInstance().newUIMode()); + } +} diff --git a/designer-form/src/test/java/com/fr/design/fit/PXReportLengthUNITTest.java b/designer-form/src/test/java/com/fr/design/fit/PXReportLengthUNITTest.java new file mode 100644 index 000000000..eef7eece9 --- /dev/null +++ b/designer-form/src/test/java/com/fr/design/fit/PXReportLengthUNITTest.java @@ -0,0 +1,49 @@ +package com.fr.design.fit; + +import com.fr.base.ScreenResolution; +import com.fr.stable.unit.FU; +import org.easymock.EasyMock; +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.powermock.api.easymock.PowerMock; +import org.powermock.core.classloader.annotations.PrepareForTest; +import org.powermock.modules.junit4.PowerMockRunner; + +/** + * Created by kerry on 2020-04-24 + */ +@RunWith(PowerMockRunner.class) +@PrepareForTest(ScreenResolution.class) +public class PXReportLengthUNITTest { + + @Test + public void testUnitText() { + PXReportLengthUNIT pxReportLengthUNIT = new PXReportLengthUNIT(); + Assert.assertEquals(com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Basic_Px"), pxReportLengthUNIT.unitText()); + } + + @Test + public void testUnitType() { + PXReportLengthUNIT pxReportLengthUNIT = new PXReportLengthUNIT(); + Assert.assertEquals(4, pxReportLengthUNIT.unitType()); + } + + @Test + public void testUnit2Value4Scale() { + PXReportLengthUNIT pxReportLengthUNIT = new PXReportLengthUNIT(); + PowerMock.mockStatic(ScreenResolution.class); + EasyMock.expect(ScreenResolution.getScreenResolution()).andReturn(96).once(); + PowerMock.replayAll(); + Assert.assertEquals(12, (int) pxReportLengthUNIT.unit2Value4Scale(FU.getInstance(457200L))); + } + + @Test + public void testFloat2UNIT() { + PowerMock.mockStatic(ScreenResolution.class); + EasyMock.expect(ScreenResolution.getScreenResolution()).andReturn(96).once(); + PowerMock.replayAll(); + PXReportLengthUNIT pxReportLengthUNIT = new PXReportLengthUNIT(); + Assert.assertEquals(457200L, pxReportLengthUNIT.float2UNIT(12.0F).toFU()); + } +} diff --git a/designer-form/src/test/java/com/fr/design/fit/PXTest.java b/designer-form/src/test/java/com/fr/design/fit/PXTest.java new file mode 100644 index 000000000..4db3e5e0a --- /dev/null +++ b/designer-form/src/test/java/com/fr/design/fit/PXTest.java @@ -0,0 +1,64 @@ +package com.fr.design.fit; + +import com.fr.base.ScreenResolution; +import com.fr.invoke.Reflect; +import org.easymock.EasyMock; +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.powermock.api.easymock.PowerMock; +import org.powermock.core.classloader.annotations.PrepareForTest; +import org.powermock.modules.junit4.PowerMockRunner; + +import java.math.BigDecimal; + +/** + * Created by kerry on 2020-04-26 + */ +@RunWith(PowerMockRunner.class) +@PrepareForTest(ScreenResolution.class) +public class PXTest { + + @Test + public void testGetScaleNumber(){ + PowerMock.mockStatic(ScreenResolution.class); + EasyMock.expect(ScreenResolution.getScreenResolution()).andReturn(96).once(); + PowerMock.replayAll(); + PX px = new PX(100F); + BigDecimal scaleNumber = Reflect.on(px).call("getScaleNumber").get(); + Assert.assertEquals(100L, scaleNumber.longValue()); + } + + @Test + public void testGetMoreFUScale() { + PowerMock.mockStatic(ScreenResolution.class); + EasyMock.expect(ScreenResolution.getScreenResolution()).andReturn(96).times(2); + PowerMock.replayAll(); + PX px = new PX(100F); + long fuScale = Reflect.on(px).call("getMoreFUScale").get(); + Assert.assertEquals(381L, fuScale); + } + + @Test + public void testToPixI(){ + PowerMock.mockStatic(ScreenResolution.class); + EasyMock.expect(ScreenResolution.getScreenResolution()).andReturn(96).times(2); + PowerMock.replayAll(); + PX px = new PX(100F); + Assert.assertEquals(100, px.toPixI(96)); + + px = new PX(19.5F); + Assert.assertEquals(19.5F, px.toPixF(96), 0.0F); + } + + @Test + public void testToPixWithResolution(){ + Assert.assertEquals(100D, PX.toPixWithResolution(100D, 96), 0.0D); + + Assert.assertEquals(19.5D, PX.toPixWithResolution(19.5D, 96), 0.0D); + + Assert.assertEquals(150D, PX.toPixWithResolution(100D, 144), 0.0D); + + Assert.assertEquals(29.25D, PX.toPixWithResolution(19.5D, 144), 0.0D); + } +} diff --git a/designer-form/src/test/java/com/fr/design/fit/common/FormDesignerUtilTest.java b/designer-form/src/test/java/com/fr/design/fit/common/FormDesignerUtilTest.java new file mode 100644 index 000000000..786d45d91 --- /dev/null +++ b/designer-form/src/test/java/com/fr/design/fit/common/FormDesignerUtilTest.java @@ -0,0 +1,38 @@ +package com.fr.design.fit.common; + +import org.junit.Assert; +import org.junit.Test; + +import java.util.ArrayList; +import java.util.List; + +/** + * Created by kerry on 2020-06-12 + */ +public class FormDesignerUtilTest { + + @Test + public void testRemoveDeletedEC(){ + List list1 = new ArrayList(); + List list2 = new ArrayList(); + list2.add("test2"); + list2.add("test3"); + list2.add("test4"); + FormDesignerUtil.removeDeletedEC(list1, list2); + Assert.assertEquals(0, list2.size()); + + list1.add("test1"); + list1.add("test3"); + list2.add("test2"); + list2.add("test3"); + list2.add("test4"); + FormDesignerUtil.removeDeletedEC(list1, list2); + Assert.assertEquals(1, list2.size()); + Assert.assertEquals("test3", list2.get(0)); + + list1.remove("test3"); + FormDesignerUtil.removeDeletedEC(list1, list2); + Assert.assertEquals(0, list2.size()); + + } +} diff --git a/designer-form/src/test/java/com/fr/design/preview/DeveloperPreviewTest.java b/designer-form/src/test/java/com/fr/design/preview/DeveloperPreviewTest.java new file mode 100644 index 000000000..624e34e59 --- /dev/null +++ b/designer-form/src/test/java/com/fr/design/preview/DeveloperPreviewTest.java @@ -0,0 +1,28 @@ +package com.fr.design.preview; + +import com.fr.design.mainframe.JForm; +import com.fr.general.web.ParameterConstants; +import org.easymock.EasyMock; +import org.junit.Assert; +import org.junit.Test; + +import java.util.Map; + +/** + * Created by kerry on 2020-04-24 + */ +public class DeveloperPreviewTest { + @Test + public void testParametersForPreview() { + DeveloperPreview formPreview = new DeveloperPreview(); + Map map = formPreview.parametersForPreview(); + Assert.assertEquals(1, map.size()); + Assert.assertEquals("developer_preview", map.get(ParameterConstants.OP)); + } + + @Test + public void testAccept() { + DeveloperPreview formPreview = new DeveloperPreview(); + Assert.assertTrue(formPreview.accept(EasyMock.mock(JForm.class))); + } +} diff --git a/designer-form/src/test/java/com/fr/design/preview/FormAdaptivePreviewTest.java b/designer-form/src/test/java/com/fr/design/preview/FormAdaptivePreviewTest.java new file mode 100644 index 000000000..4972568b4 --- /dev/null +++ b/designer-form/src/test/java/com/fr/design/preview/FormAdaptivePreviewTest.java @@ -0,0 +1,28 @@ +package com.fr.design.preview; + +import com.fr.design.mainframe.JForm; +import com.fr.general.web.ParameterConstants; +import org.easymock.EasyMock; +import org.junit.Assert; +import org.junit.Test; + +import java.util.Map; + +/** + * Created by kerry on 2020-04-24 + */ +public class FormAdaptivePreviewTest { + @Test + public void testParametersForPreview() { + FormAdaptivePreview formPreview = new FormAdaptivePreview(); + Map map = formPreview.parametersForPreview(); + Assert.assertEquals(1, map.size()); + Assert.assertEquals("form_adaptive", map.get(ParameterConstants.OP)); + } + + @Test + public void testAccept() { + FormAdaptivePreview formPreview = new FormAdaptivePreview(); + Assert.assertTrue(formPreview.accept(EasyMock.mock(JForm.class))); + } +} diff --git a/designer-realize/src/main/java/com/fr/design/actions/columnrow/ColumnRowSizingAction.java b/designer-realize/src/main/java/com/fr/design/actions/columnrow/ColumnRowSizingAction.java index 0903323b8..794f1febb 100644 --- a/designer-realize/src/main/java/com/fr/design/actions/columnrow/ColumnRowSizingAction.java +++ b/designer-realize/src/main/java/com/fr/design/actions/columnrow/ColumnRowSizingAction.java @@ -4,6 +4,7 @@ package com.fr.design.actions.columnrow; import com.fr.design.DesignerEnvManager; +import com.fr.design.fit.DesignerUIModeConfig; import com.fr.design.fun.ReportLengthUNITProvider; import com.fr.design.gui.frpane.UnitInputPane; import com.fr.design.gui.frpane.UnitInputPane.ValueNotChangeException; @@ -11,8 +12,6 @@ import com.fr.design.mainframe.DesignerContext; import com.fr.design.dialog.DialogActionAdapter; import com.fr.design.mainframe.ElementCasePane; -import com.fr.design.fun.ReportLengthUNITProvider; -import com.fr.design.unit.UnitConvertUtil; import com.fr.general.ComparatorUtils; import com.fr.grid.selection.CellSelection; import com.fr.report.elementcase.ElementCase; @@ -51,7 +50,7 @@ public abstract class ColumnRowSizingAction extends AbstractColumnRowIndexAction try { float newHeight = (float) uPane.update(); int unitType = DesignerEnvManager.getEnvManager().getReportLengthUnit(); - ReportLengthUNITProvider lengthUNIT = UnitConvertUtil.parseLengthUNIT(unitType); + ReportLengthUNITProvider lengthUNIT = DesignerUIModeConfig.getInstance().parseLengthUNIT(unitType); UNIT newLen = lengthUNIT.float2UNIT(newHeight); if (!ComparatorUtils.equals(oldLen, newLen)) { updateAction(report, newLen, finalCS); @@ -68,7 +67,7 @@ public abstract class ColumnRowSizingAction extends AbstractColumnRowIndexAction protected void populateNumberDialog(final UnitInputPane uPane, UNIT unit) { int unitType = DesignerEnvManager.getEnvManager().getReportLengthUnit(); - ReportLengthUNITProvider lengthUNIT = UnitConvertUtil.parseLengthUNIT(unitType); + ReportLengthUNITProvider lengthUNIT = DesignerUIModeConfig.getInstance().parseLengthUNIT(unitType); float va = lengthUNIT.unit2Value4Scale(unit); uPane.setUnitText(lengthUNIT.unitText()); uPane.populate(va); diff --git a/designer-realize/src/main/java/com/fr/design/condition/WHPane.java b/designer-realize/src/main/java/com/fr/design/condition/WHPane.java index 7b4f636a6..0b7fc7c01 100644 --- a/designer-realize/src/main/java/com/fr/design/condition/WHPane.java +++ b/designer-realize/src/main/java/com/fr/design/condition/WHPane.java @@ -1,10 +1,10 @@ package com.fr.design.condition; import com.fr.design.DesignerEnvManager; +import com.fr.design.fit.DesignerUIModeConfig; +import com.fr.design.fun.ReportLengthUNITProvider; import com.fr.design.gui.ilable.UILabel; import com.fr.design.gui.ispinner.UIBasicSpinner; -import com.fr.design.fun.ReportLengthUNITProvider; -import com.fr.design.unit.UnitConvertUtil; import com.fr.design.utils.gui.GUICoreUtils; import com.fr.report.cell.cellattr.highlight.HighlightAction; @@ -47,7 +47,7 @@ public abstract class WHPane extends ConditionAttrSingleConditionPane= 0 && e.getY() < height) { + Point convertPoint = new Point(width, 0); + SwingUtilities.convertPointToScreen(convertPoint, grid); + showToolTip(grid, e, actualWidth + com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Basic_Px"), convertPoint); + return; + } else { + hideToolTip(); + } + if (Math.abs(e.getY() - height) < 5 && e.getX() >= 0 && e.getX() < width) { + Point convertPoint = new Point(0, height); + SwingUtilities.convertPointToScreen(convertPoint, grid); + showToolTip(grid, e, actualHeight + com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Basic_Px"), convertPoint); + return; + } else { + hideToolTip(); + } + } + + private void showToolTip(Grid grid, MouseEvent evt, String text, Point tipLocation) { + if (tipWindow == null) { + tipWindow = new JWindow(); + + + tip = grid.createToolTip(); + tip.setBorder(BorderFactory.createEmptyBorder()); + tipWindow.getContentPane().add(tip, BorderLayout.CENTER); + } + tip.setTipText(text); + tip.setForeground(Color.decode("#88ACC6")); + tipWindow.setLocation(tipLocation.x, tipLocation.y); + tipWindow.pack(); + tipWindow.setVisible(true); + } + + private void hideToolTip() { + if (tipWindow != null) { + tipWindow.setVisible(false); + } + } + + @Override + public void mouseClicked(MouseEvent e) { + + } + + @Override + public void mousePressed(MouseEvent e) { + + } + + @Override + public void mouseReleased(MouseEvent e) { + + } + + @Override + public void mouseEntered(MouseEvent e) { + + } + + @Override + public void mouseExited(MouseEvent e) { + hideToolTip(); + } + + @Override + public void mouseWheelMoved(MouseWheelEvent e) { + hideToolTip(); + } +} diff --git a/designer-realize/src/main/java/com/fr/design/fit/grid/ElementCasePaneUtil.java b/designer-realize/src/main/java/com/fr/design/fit/grid/ElementCasePaneUtil.java new file mode 100644 index 000000000..1078250c6 --- /dev/null +++ b/designer-realize/src/main/java/com/fr/design/fit/grid/ElementCasePaneUtil.java @@ -0,0 +1,186 @@ +package com.fr.design.fit.grid; + +import com.fr.base.vcs.DesignerMode; +import com.fr.design.actions.cell.CleanAuthorityAction; +import com.fr.design.actions.columnrow.CancelColumnAction; +import com.fr.design.actions.columnrow.CancelRowAction; +import com.fr.design.actions.columnrow.ColumnHideAction; +import com.fr.design.actions.columnrow.ColumnWidthAction; +import com.fr.design.actions.columnrow.DeleteColumnAction; +import com.fr.design.actions.columnrow.DeleteRowAction; +import com.fr.design.actions.columnrow.FootColumnAction; +import com.fr.design.actions.columnrow.FootRowAction; +import com.fr.design.actions.columnrow.HeadColumnAction; +import com.fr.design.actions.columnrow.HeadRowAction; +import com.fr.design.actions.columnrow.InsertColumnAction; +import com.fr.design.actions.columnrow.InsertRowAction; +import com.fr.design.actions.columnrow.ResetColumnHideAction; +import com.fr.design.actions.columnrow.ResetRowHideAction; +import com.fr.design.actions.columnrow.RowHeightAction; +import com.fr.design.actions.columnrow.RowHideAction; +import com.fr.design.gui.imenu.UIPopupMenu; +import com.fr.design.mainframe.ElementCasePane; +import com.fr.grid.GridUtils; +import com.fr.grid.selection.CellSelection; +import com.fr.page.ReportPageAttrProvider; +import com.fr.report.elementcase.ElementCase; +import com.fr.stable.ColumnRow; + +import javax.swing.JPopupMenu; +import java.awt.event.MouseEvent; + +/** + * Created by kerry on 2020-04-28 + */ +public class ElementCasePaneUtil { + public static UIPopupMenu createColumnPopupMenu(ElementCasePane reportPane, MouseEvent evt, int selectedColumn) { + UIPopupMenu popupMenu = new UIPopupMenu(); + + if (DesignerMode.isAuthorityEditing()) { + popupMenu.add(new CleanAuthorityAction(reportPane).createMenuItem()); + return popupMenu; + } + + InsertColumnAction insertColumnAction = new InsertColumnAction(reportPane, com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Report_Utils_Insert_Column")); + + DeleteColumnAction deleteColumnAction = new DeleteColumnAction(reportPane, com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Report_Utils_Delete_Column")); + + ColumnWidthAction columnWidthAction = new ColumnWidthAction(reportPane, selectedColumn); + + ColumnHideAction columnHideAction = new ColumnHideAction(reportPane, selectedColumn); + + ResetColumnHideAction resetRowHideAction = new ResetColumnHideAction(reportPane, selectedColumn); + popupMenu.add(insertColumnAction.createMenuItem()); + popupMenu.add(deleteColumnAction.createMenuItem()); + popupMenu.addSeparator(); + popupMenu.add(columnWidthAction.createMenuItem()); + + CellSelection cs = (CellSelection) reportPane.getSelection(); + addColumnMenu(reportPane, popupMenu, evt, cs.getColumn(), cs.getColumn() + cs.getColumnSpan() - 1); + popupMenu.add(columnHideAction.createMenuItem()); + popupMenu.add(resetRowHideAction.createMenuItem()); + + return popupMenu; + } + + private static void addColumnMenu(ElementCasePane reportPane, UIPopupMenu popupMenu, MouseEvent evt, int selectedColumnsFrom, int selectedColumnsTo) { + HeadColumnAction headcolumnAction = new HeadColumnAction(reportPane); + FootColumnAction footcolumnAction = new FootColumnAction(reportPane); + + ColumnRow selectedCellPoint = GridUtils.getAdjustEventColumnRow_withresolution(reportPane, evt.getX(), evt.getY(), reportPane.getResolution()); + ElementCase elementCase = reportPane.getEditingElementCase(); + boolean cancel = false; + ReportPageAttrProvider reportPageAttr = elementCase.getReportPageAttr(); + + if (reportPageAttr != null) { + popupMenu.addSeparator(); + popupMenu.add(headcolumnAction.createMenuItem()); + popupMenu.add(footcolumnAction.createMenuItem()); + int from = reportPageAttr.getRepeatHeaderColumnFrom(); + int to = reportPageAttr.getRepeatHeaderColumnTo(); + int from2 = reportPageAttr.getRepeatFooterColumnFrom(); + int to2 = reportPageAttr.getRepeatFooterColumnTo(); + int column = selectedCellPoint.getColumn(); + + if (column >= selectedColumnsFrom && column <= selectedColumnsTo) { + cancel = isCancel(elementCase, selectedColumnsFrom, selectedColumnsTo, from, to, from2, to2); + } else { + + if (elementCase.getReportPageAttr() != null) { + + if (column == from || column == to || column == from2 || column == to2) { + cancel = true; + } + } + } + if (cancel) { + CancelColumnAction cancelAction = new CancelColumnAction(reportPane); + popupMenu.add(cancelAction.createMenuItem()); + } + } + } + + private static boolean isCancel(ElementCase report, int selectedColumnsFrom, int selectedColumnsTo, int from, int to, int from2, int to2) { + + boolean cancel = false; + + if (report.getReportPageAttr() != null) { + + if (from == selectedColumnsFrom && to == selectedColumnsTo) { + cancel = true; + } + + if (from2 == selectedColumnsFrom && to2 == selectedColumnsTo) { + cancel = true; + } + } + return cancel; + } + + public static UIPopupMenu createRowPopupMenu(ElementCasePane reportPane, MouseEvent evt, int selectedRows) { + UIPopupMenu popupMenu = new UIPopupMenu(); + if (DesignerMode.isAuthorityEditing()) { + popupMenu.add(new CleanAuthorityAction(reportPane).createMenuItem()); + return popupMenu; + } + + InsertRowAction insertRowAction = new InsertRowAction(reportPane, com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Report_Utils_Insert_Row")); + + DeleteRowAction deleteRowAction = new DeleteRowAction(reportPane, com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Basic_Utils_Delete_Row")); + + RowHeightAction rowHeightAction = new RowHeightAction(reportPane, selectedRows); + + RowHideAction rowHideAction = new RowHideAction(reportPane, selectedRows); + + ResetRowHideAction resetRowHideAction = new ResetRowHideAction(reportPane, selectedRows); + popupMenu.add(insertRowAction.createMenuItem()); + popupMenu.add(deleteRowAction.createMenuItem()); + popupMenu.addSeparator(); + popupMenu.add(rowHeightAction.createMenuItem()); + CellSelection cs = (CellSelection) reportPane.getSelection(); + addRowMenu(reportPane, popupMenu, evt, cs.getRow(), cs.getRow() + cs.getRowSpan() - 1); + popupMenu.add(rowHideAction.createMenuItem()); + popupMenu.add(resetRowHideAction.createMenuItem()); + return popupMenu; + } + + private static void addRowMenu(ElementCasePane reportPane, JPopupMenu popupMenu, MouseEvent evt, int selectedRowsFrom, int selectedRowsTo) { + HeadRowAction headrowAction = new HeadRowAction(reportPane); + FootRowAction footrowAction = new FootRowAction(reportPane); + + ElementCase elementCase = reportPane.getEditingElementCase(); + boolean cancel = false; + ColumnRow selectedCellPoint = GridUtils.getAdjustEventColumnRow_withresolution(reportPane, evt.getX(), evt.getY(), reportPane.getResolution()); + ReportPageAttrProvider reportPageAttr = elementCase.getReportPageAttr(); + ElementCase report = reportPane.getEditingElementCase(); + if (reportPageAttr != null) { + popupMenu.addSeparator(); + popupMenu.add(headrowAction.createMenuItem()); + popupMenu.add(footrowAction.createMenuItem()); + int from = report.getReportPageAttr().getRepeatHeaderRowFrom(); + int to = report.getReportPageAttr().getRepeatHeaderRowTo(); + int from2 = report.getReportPageAttr().getRepeatFooterRowFrom(); + int to2 = report.getReportPageAttr().getRepeatFooterRowTo(); + + if (selectedCellPoint.getRow() >= selectedRowsFrom && selectedCellPoint.getRow() <= selectedRowsTo) { + + cancel = isCancel(report, selectedRowsFrom, selectedRowsTo, from, to, from2, to2); + + } else { + + int row = selectedCellPoint.getRow(); + if (report.getReportPageAttr() != null) { + if (row == from || row == to || row == from2 || row == to2) { + cancel = true; + } + } + } + + if (cancel) { + CancelRowAction cancelAction = new CancelRowAction(reportPane); + popupMenu.add(cancelAction.createMenuItem()); + } + } + } + +} diff --git a/designer-realize/src/main/java/com/fr/design/fit/grid/GridHeaderWithBoundMouseHandler.java b/designer-realize/src/main/java/com/fr/design/fit/grid/GridHeaderWithBoundMouseHandler.java new file mode 100644 index 000000000..bd3c73846 --- /dev/null +++ b/designer-realize/src/main/java/com/fr/design/fit/grid/GridHeaderWithBoundMouseHandler.java @@ -0,0 +1,70 @@ +package com.fr.design.fit.grid; + +import com.fr.base.DynamicUnitList; +import com.fr.design.fit.DesignerUIModeConfig; +import com.fr.design.mainframe.ElementCasePane; +import com.fr.grid.GridHeader; +import com.fr.grid.AbstractGridHeaderMouseHandler; +import com.fr.report.elementcase.ElementCase; +import com.fr.report.elementcase.TemplateElementCase; + +import java.awt.event.MouseEvent; + +public abstract class GridHeaderWithBoundMouseHandler extends AbstractGridHeaderMouseHandler{ + protected static final int FUZZY_EDGE = 10; + private int limit; + + public GridHeaderWithBoundMouseHandler(GridHeader gHeader, int limit) { + super(gHeader); + this.limit = limit; + } + + public void setLimit(int limit) { + this.limit = limit; + } + + public int getLimit() { + return limit; + } + + public int getDragIndex(MouseEvent evt) { + ElementCase report = this.getEditingElementCase(); + DynamicUnitList sizeList = getSizeList(report); + + int scrollValue = getScrollValue(this.getElementCasePane()); + int scrollExtent = getScrollExtent(this.getElementCasePane()); + int endValue = scrollValue + scrollExtent + 1; + + int beginValue = getBeginValue(this.getElementCasePane()); + + double tmpSize1 = 0; + double tmpSize2; + double tmpIncreaseSize = 0; + + int resolution = DesignerUIModeConfig.getInstance().getScreenResolution(); + + for (int index = beginValue; index < endValue; index++) { + if (index == 0) { + index = scrollValue; + } + + tmpSize1 += tmpIncreaseSize; + tmpIncreaseSize = sizeList.get(index).toPixD(resolution); + tmpSize2 = tmpSize1 + Math.max(1, tmpIncreaseSize); + if (isOnSeparatorLineIncludeZero(evt, tmpSize2, tmpIncreaseSize) || isOnNormalSeparatorLine(evt, tmpSize2)) { + return index; + } + } + return -1; + } + + + public ElementCasePane getElementCasePane() { + return this.gHeader.getElementCasePane(); + } + + public TemplateElementCase getEditingElementCase() { + return this.getElementCasePane().getEditingElementCase(); + } + +} diff --git a/designer-realize/src/main/java/com/fr/design/fit/grid/GridLimitColumnMouseHandler.java b/designer-realize/src/main/java/com/fr/design/fit/grid/GridLimitColumnMouseHandler.java new file mode 100644 index 000000000..28185bc38 --- /dev/null +++ b/designer-realize/src/main/java/com/fr/design/fit/grid/GridLimitColumnMouseHandler.java @@ -0,0 +1,194 @@ +package com.fr.design.fit.grid; + +import com.fr.base.DynamicUnitList; +import com.fr.design.fit.DesignerUIModeConfig; +import com.fr.design.gui.imenu.UIPopupMenu; +import com.fr.design.mainframe.ElementCasePane; +import com.fr.grid.GridColumn; +import com.fr.grid.GridHeader; +import com.fr.grid.GridUtils; +import com.fr.grid.selection.CellSelection; +import com.fr.grid.selection.Selection; +import com.fr.report.ReportHelper; +import com.fr.report.elementcase.ElementCase; +import com.fr.stable.ColumnRow; +import com.fr.stable.unit.FU; +import com.fr.stable.unit.UNIT; + +import javax.swing.SwingUtilities; +import java.awt.Dimension; +import java.awt.Point; +import java.awt.Rectangle; +import java.awt.Toolkit; +import java.awt.event.MouseEvent; + +/** + * peter:处理对GridColumn的Mouse事件. + */ +public class GridLimitColumnMouseHandler extends GridHeaderWithBoundMouseHandler { + + + public GridLimitColumnMouseHandler(GridColumn gridColumn, int limit) { + super(gridColumn, limit); + this.resolution = gridColumn.getResolution(); + } + + @Override + protected void resetSelectionByRightButton(ColumnRow selectedCellPoint, Selection cs, ElementCasePane ePane) { + int[] selectedColumns = cs.getSelectedColumns(); + if (selectedColumns.length == 0 + || selectedCellPoint.getColumn() < selectedColumns[0] + || selectedCellPoint.getColumn() > selectedColumns[selectedColumns.length - 1]) { + resetGridSelectionBySelect(selectedCellPoint.getColumn(), ePane); + } + } + + + protected int doChooseFrom() { + return CellSelection.CHOOSE_COLUMN; + } + + @Override + protected Rectangle resetSelectedBoundsByShift(Rectangle editRectangle, ColumnRow selectedCellPoint, ElementCasePane reportPane) { + int tempOldSelectedCellX = editRectangle.x;// editRectangle.x; + + // adjust them to got the correct selected bounds. + if (selectedCellPoint.getColumn() >= editRectangle.x) { + selectedCellPoint = ColumnRow.valueOf(selectedCellPoint.getColumn() + 1, selectedCellPoint.getRow()); + } else { + tempOldSelectedCellX++; + } + + int lastRow = GridUtils.getAdjustLastColumnRowOfReportPane(reportPane).getRow(); + return new Rectangle(Math.min(tempOldSelectedCellX, selectedCellPoint.getColumn()), 0, Math.max(editRectangle.width, Math.abs(tempOldSelectedCellX + - selectedCellPoint.getColumn())), lastRow); + } + + @Override + protected int[] getGridSelectionIndices(CellSelection cs) { + return cs.getSelectedColumns(); + } + + @Override + protected int getScrollValue(ElementCasePane casePane) { + return casePane.getGrid().getHorizontalValue(); + } + + @Override + protected int getScrollExtent(ElementCasePane casePane) { + return casePane.getGrid().getHorizontalExtent(); + } + + @Override + protected int getBeginValue(ElementCasePane casePane) { + return casePane.getGrid().getHorizontalBeginValue(); + } + + @Override + protected int getColumnOrRowByGridHeader(ColumnRow selectedCellPoint) { + return selectedCellPoint.getColumn(); + } + + + /** + * Checks whether is on zero separator line. + */ + @Override + protected boolean isOnSeparatorLineIncludeZero(MouseEvent evt, double tmpWidth2, double tmpIncreaseWidth) { + return tmpIncreaseWidth <= 1 && (evt.getX() >= tmpWidth2 + 2 && (evt.getX() <= tmpWidth2 + SEPARATOR_GAP)); + } + + @Override + protected boolean between(MouseEvent evt, double from, double to) { + return evt.getX() > from && evt.getX() <= to; + } + + /** + * Checks whether is on normal separator line. + */ + @Override + protected boolean isOnNormalSeparatorLine(MouseEvent evt, double tmpWidth2) { + return (evt.getX() >= tmpWidth2 - 2) && (evt.getX() <= tmpWidth2 + 2); + } + + @Override + protected int evtOffset(MouseEvent evt, int offset) { + return evt.getX() - offset; + } + + @Override + protected DynamicUnitList getSizeList(ElementCase elementCase) { + return ReportHelper.getColumnWidthList(elementCase); + } + + @Override + protected String methodName() { + return "setColumnWidth"; + } + + @Override + protected String getSelectedHeaderTooltip(int selectedColumnCount) { + return selectedColumnCount + "C"; + } + + @Override + protected Point getTipLocationByMouseEvent(MouseEvent evt, GridHeader gHeader, Dimension tipPreferredSize) { + Point convertPoint = new Point(evt.getX(), 0); + SwingUtilities.convertPointToScreen(convertPoint, gHeader); + + Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize(); + convertPoint.x = Math.max(0, Math.min(convertPoint.x - tipPreferredSize.width / 2, screenSize.width - tipPreferredSize.width)); + convertPoint.y = convertPoint.y - tipPreferredSize.height - 2; + + return convertPoint; + } + + @Override + protected void resetGridSelectionBySelect(int column, ElementCasePane ePane) { + int lastRow = GridUtils.getAdjustLastColumnRowOfReportPane(ePane).getRow(); + CellSelection cellSelection = new CellSelection(column, 0, 1, lastRow); + cellSelection.setSelectedType(CellSelection.CHOOSE_COLUMN); + ePane.setSelection(cellSelection); + } + + @Override + protected String nameOfMoveCursorGIF() { + return "cursor_hmove"; + } + + @Override + protected String nameOfSelectCursorGIF() { + return "cursor_hselect"; + } + + @Override + protected String nameOfSplitCursorGIF() { + return "cursor_hsplit"; + } + + @Override + protected UIPopupMenu createPopupMenu(ElementCasePane reportPane, + MouseEvent evt, int columnIndex) { + return ElementCasePaneUtil.createColumnPopupMenu(reportPane, evt, columnIndex); + } + + @Override + protected void resetGridSelectionByDrag(CellSelection gridSelection, ElementCasePane reportPane, + int startMultiSelectIndex, int endMultiSelectIndex) { + int lastRow = GridUtils.getAdjustLastColumnRowOfReportPane(reportPane).getRow(); + gridSelection.setLastRectangleBounds(Math.min(endMultiSelectIndex, startMultiSelectIndex), 0, Math.abs(startMultiSelectIndex - endMultiSelectIndex) + 1, lastRow); + } + + @Override + public void mouseReleased(MouseEvent e) { + super.mouseReleased(e); + int resolution = DesignerUIModeConfig.getInstance().getScreenResolution(); + int dragIndex = getDragIndex(e); + if (Math.abs(e.getX() - getLimit()) < FUZZY_EDGE && dragIndex >= 0) { + UNIT oldValue = this.getEditingElementCase().getColumnWidth(dragIndex); + this.getEditingElementCase().setColumnWidth(dragIndex, FU.valueOfPix(oldValue.toPixI(resolution) + getLimit() - e.getX(), resolution)); + } + this.getElementCasePane().repaint(); + } + +} diff --git a/designer-realize/src/main/java/com/fr/design/fit/grid/GridLimitRowMouseHandler.java b/designer-realize/src/main/java/com/fr/design/fit/grid/GridLimitRowMouseHandler.java new file mode 100644 index 000000000..ac4a70eab --- /dev/null +++ b/designer-realize/src/main/java/com/fr/design/fit/grid/GridLimitRowMouseHandler.java @@ -0,0 +1,190 @@ +package com.fr.design.fit.grid; + +import com.fr.base.DynamicUnitList; +import com.fr.design.fit.DesignerUIModeConfig; +import com.fr.design.gui.imenu.UIPopupMenu; +import com.fr.design.mainframe.ElementCasePane; +import com.fr.grid.GridHeader; +import com.fr.grid.GridRow; +import com.fr.grid.GridUtils; +import com.fr.grid.selection.CellSelection; +import com.fr.grid.selection.Selection; +import com.fr.report.ReportHelper; +import com.fr.report.elementcase.ElementCase; +import com.fr.stable.ColumnRow; +import com.fr.stable.unit.FU; +import com.fr.stable.unit.UNIT; + +import javax.swing.SwingUtilities; +import java.awt.Dimension; +import java.awt.Point; +import java.awt.Rectangle; +import java.awt.event.MouseEvent; + +/** + * peter:处理对GridRow的Mouse事件. + */ +public class GridLimitRowMouseHandler extends GridHeaderWithBoundMouseHandler { + + public GridLimitRowMouseHandler(GridRow gridRow, int limit) { + super(gridRow, limit); + } + + @Override + protected void resetSelectionByRightButton(ColumnRow selectedCellPoint, Selection cs, ElementCasePane ePane) { + int[] selectedRows = cs.getSelectedRows(); + if (selectedRows.length == 0 + || selectedCellPoint.getRow() < selectedRows[0] + || selectedCellPoint.getRow() > selectedRows[selectedRows.length - 1]) { + resetGridSelectionBySelect(selectedCellPoint.getRow(), ePane); + } + } + + + protected int doChooseFrom() { + return CellSelection.CHOOSE_ROW; + } + + @Override + protected int getScrollValue(ElementCasePane casePane) { + return casePane.getGrid().getVerticalValue(); + } + + @Override + protected int getScrollExtent(ElementCasePane casePane) { + return casePane.getGrid().getVerticalExtent(); + } + + @Override + protected int getBeginValue(ElementCasePane casePane) { + return casePane.getGrid().getVerticalBeginValue(); + } + + @Override + protected Rectangle resetSelectedBoundsByShift(Rectangle editRectangle, ColumnRow selectedCellPoint, ElementCasePane reportPane) { + int tempOldSelectedCellY = editRectangle.y;// editRectangle.x; + + // ajust them to got the correct selected bounds. + if (selectedCellPoint.getRow() >= editRectangle.y) { + selectedCellPoint = ColumnRow.valueOf(selectedCellPoint.getColumn(), selectedCellPoint.getRow() + 1); + } else { + tempOldSelectedCellY++; + } + + int lastColumn = GridUtils.getAdjustLastColumnRowOfReportPane(reportPane).getColumn(); + return new Rectangle(0, Math.min(tempOldSelectedCellY, selectedCellPoint.getRow()), + lastColumn, Math.max(editRectangle.height, Math.abs(tempOldSelectedCellY - selectedCellPoint.getRow()))); + } + + @Override + protected int[] getGridSelectionIndices(CellSelection cs) { + return cs.getSelectedRows(); + } + + @Override + protected int getColumnOrRowByGridHeader(ColumnRow selectedCellPoint) { + return selectedCellPoint.getRow(); + } + + + @Override + protected void resetGridSelectionBySelect(int row, ElementCasePane ePane) { + int lastColumn = GridUtils.getAdjustLastColumnRowOfReportPane(ePane).getColumn(); + CellSelection cellSelection = new CellSelection(0, row, lastColumn, 1); + cellSelection.setSelectedType(CellSelection.CHOOSE_ROW); + ePane.setSelection(cellSelection); + } + + /** + * Checks whether is on zero separator line. + */ + @Override + protected boolean isOnSeparatorLineIncludeZero(MouseEvent evt, double tmpHeight2, double tmpIncreaseHeight) { + return tmpIncreaseHeight <= 1 && (evt.getY() >= tmpHeight2 + 2 && evt.getY() <= tmpHeight2 + SEPARATOR_GAP); + } + + @Override + protected boolean between(MouseEvent evt, double from, double to) { + return evt.getY() > from && evt.getY() <= to; + } + + @Override + protected DynamicUnitList getSizeList(ElementCase elementCase) { + return ReportHelper.getRowHeightList(elementCase); + } + + @Override + protected String methodName() { + return "setRowHeight"; + } + + /** + * Checks whether is on normal separator line. + */ + @Override + protected boolean isOnNormalSeparatorLine(MouseEvent evt, double tmpHeight2) { + return (evt.getY() >= tmpHeight2 - 2) && (evt.getY() <= tmpHeight2 + 2); + } + + @Override + protected int evtOffset(MouseEvent evt, int offset) { + return evt.getY() - offset; + } + + @Override + protected String getSelectedHeaderTooltip(int rowSelectedCount) { + return rowSelectedCount + "R"; + } + + @Override + protected Point getTipLocationByMouseEvent(MouseEvent evt, GridHeader gHeader, Dimension tipPreferredSize) { + Point convertPoint = new Point(0, evt.getY()); + SwingUtilities.convertPointToScreen(convertPoint, gHeader); + + convertPoint.x = convertPoint.x + gHeader.getSize().width + 2; + convertPoint.y = convertPoint.y - tipPreferredSize.height / 2; + + return convertPoint; + } + + @Override + protected String nameOfMoveCursorGIF() { + return "cursor_vmove"; + } + + @Override + protected String nameOfSelectCursorGIF() { + return "cursor_vselect"; + } + + @Override + protected String nameOfSplitCursorGIF() { + return "cursor_vsplit"; + } + + @Override + protected UIPopupMenu createPopupMenu(ElementCasePane reportPane, + MouseEvent evt, int rowIndex) { + return ElementCasePaneUtil.createRowPopupMenu(reportPane, evt, rowIndex); + } + + @Override + protected void resetGridSelectionByDrag(CellSelection gridSelection, ElementCasePane reportPane, + int startMultiSelectIndex, int endMultiSelectIndex) { + int lastColumn = GridUtils.getAdjustLastColumnRowOfReportPane(reportPane).getColumn(); + gridSelection.setLastRectangleBounds(0, Math.min(endMultiSelectIndex, startMultiSelectIndex), lastColumn, Math.abs(startMultiSelectIndex - endMultiSelectIndex) + 1); + + } + + @Override + public void mouseReleased(MouseEvent e) { + super.mouseReleased(e); + int resolution = DesignerUIModeConfig.getInstance().getScreenResolution(); + int dragIndex = getDragIndex(e); + if (Math.abs(e.getY() - getLimit()) < FUZZY_EDGE && dragIndex >= 0) { + UNIT oldValue = this.getEditingElementCase().getRowHeight(dragIndex); + this.getEditingElementCase().setRowHeight(dragIndex, FU.valueOfPix(oldValue.toPixI(resolution) + getLimit() - e.getY(), resolution)); + } + this.getElementCasePane().repaint(); + } +} diff --git a/designer-realize/src/main/java/com/fr/design/fit/grid/NewFormDesignerGridUI.java b/designer-realize/src/main/java/com/fr/design/fit/grid/NewFormDesignerGridUI.java new file mode 100644 index 000000000..c3cc4f53d --- /dev/null +++ b/designer-realize/src/main/java/com/fr/design/fit/grid/NewFormDesignerGridUI.java @@ -0,0 +1,190 @@ +package com.fr.design.fit.grid; + +import com.fr.design.designer.creator.XElementCase; +import com.fr.design.fit.AdaptiveCellElementPainter; +import com.fr.design.fit.common.FormDesignerUtil; +import com.fr.design.mainframe.ElementCasePane; +import com.fr.design.mainframe.FormDesigner; +import com.fr.grid.CellElementPainter; +import com.fr.grid.Grid; +import com.fr.grid.GridColumn; +import com.fr.grid.GridRow; +import com.fr.grid.GridUI; +import com.fr.report.elementcase.TemplateElementCase; +import com.fr.report.worksheet.FormElementCase; +import com.fr.stable.Constants; +import com.fr.stable.GraphDrawHelper; + +import javax.swing.JComponent; +import java.awt.Color; +import java.awt.Graphics; +import java.awt.Graphics2D; +import java.awt.Insets; +import java.awt.Rectangle; +import java.awt.event.MouseListener; +import java.awt.event.MouseMotionListener; +import java.awt.event.MouseWheelListener; + + +/** + * Created by kerry on 2020-04-14 + */ +public class NewFormDesignerGridUI extends GridUI { + private GridLimitColumnMouseHandler gridColumnMouseHandler; + private GridLimitRowMouseHandler gridRowMouseHandler; + private AdaptiveGridListener adaptiveGridListener; + private FormDesigner designer; + + public NewFormDesignerGridUI(FormDesigner designer, int resolution) { + super(resolution); + this.designer = designer; + this.setCellElementPainter(new AdaptiveCellElementPainter()); + } + + public void setCellElementPainter(CellElementPainter elementPainter) { + this.painter = elementPainter; + } + + public void paint(Graphics g, JComponent c) { + Graphics2D g2d = (Graphics2D) g; + Grid grid = (Grid) c; + // 取得ElementCasePane.ElementCase + ElementCasePane elementCasePane = grid.getElementCasePane(); + final TemplateElementCase elementCase = elementCasePane.getEditingElementCase(); + + super.paint(g, c); + + if (!(elementCase instanceof FormElementCase)) { + return; + } + final Rectangle rectangle = getBoundsLineRect(elementCase, grid); + int width = rectangle.width - columnWidthList.getRangeValue(0, horizontalValue).toPixI(resolution); + int height = rectangle.height - rowHeightList.getRangeValue(0, verticalValue).toPixI(resolution); + drawBoundsLine(g2d, width, height); + addListener(grid, elementCasePane, width, height, rectangle.width, rectangle.height); + } + + /** + * 获取需要画线的矩形大小 + */ + private Rectangle getBoundsLineRect(TemplateElementCase elementCase, Grid grid) { + final Rectangle rectangle = new Rectangle(); + XElementCase xElementCase = FormDesignerUtil.getXelementCase(designer.getRootComponent(), (FormElementCase) elementCase); + if (xElementCase != null) { + rectangle.setBounds(xElementCase.getBounds()); + + //减去内边距的宽和高 + Insets insets = xElementCase.getInsets(); + rectangle.width -= insets.left + insets.right; + rectangle.height -= insets.top + insets.bottom; + + } + return rectangle; + } + + + private void addListener(Grid grid, ElementCasePane elementCasePane, int width, int height, int actualWidth, int actualHeight) { + addGridColumnListener(elementCasePane.getGridColumn(), width); + addGridRowListener(elementCasePane.getGridRow(), height); + addMouseListener(grid, width, height, actualWidth, actualHeight); + } + + + private void drawBoundsLine(Graphics2D g2d, int width, int height) { + g2d.setPaint(Color.black); + g2d.setStroke(GraphDrawHelper.getStroke(Constants.LINE_DASH_DOT)); + g2d.drawLine(0, height, width, height); + g2d.drawLine(width, 0, width, height); + } + + private void removeGridColumnListener(GridColumn column) { + MouseMotionListener[] mouseMotionListeners = column.getMouseMotionListeners(); + for (MouseMotionListener mouseMotionListener : mouseMotionListeners) { + if (mouseMotionListener instanceof com.fr.grid.GridColumnMouseHandler || mouseMotionListener instanceof GridLimitColumnMouseHandler) { + column.removeMouseMotionListener(mouseMotionListener); + } + } + MouseListener[] mouseListeners = column.getMouseListeners(); + for (MouseListener motionListener : mouseListeners) { + if (motionListener instanceof com.fr.grid.GridColumnMouseHandler || motionListener instanceof GridLimitColumnMouseHandler) { + column.removeMouseListener(motionListener); + } + } + } + + private void removeGridRowListener(GridRow row) { + MouseMotionListener[] mouseMotionListeners = row.getMouseMotionListeners(); + for (MouseMotionListener mouseMotionListener : mouseMotionListeners) { + if (mouseMotionListener instanceof com.fr.grid.GridRowMouseHandler || mouseMotionListener instanceof GridLimitRowMouseHandler) { + row.removeMouseMotionListener(mouseMotionListener); + } + } + MouseListener[] mouseListeners = row.getMouseListeners(); + for (MouseListener motionListener : mouseListeners) { + if (motionListener instanceof com.fr.grid.GridRowMouseHandler || motionListener instanceof GridLimitRowMouseHandler) { + row.removeMouseListener(motionListener); + } + } + } + + private void removeGridListener(Grid grid) { + MouseMotionListener[] mouseMotionListeners = grid.getMouseMotionListeners(); + for (MouseMotionListener mouseMotionListener : mouseMotionListeners) { + if (mouseMotionListener instanceof AdaptiveGridListener) { + grid.removeMouseMotionListener(mouseMotionListener); + break; + } + } + MouseListener[] mouseListeners = grid.getMouseListeners(); + for (MouseListener motionListener : mouseListeners) { + if (motionListener instanceof AdaptiveGridListener) { + grid.removeMouseListener(motionListener); + break; + } + } + MouseWheelListener[] mouseWheelListeners = grid.getMouseWheelListeners(); + for (MouseWheelListener mouseWheelListener : mouseWheelListeners) { + if (mouseWheelListener instanceof AdaptiveGridListener) { + grid.removeMouseWheelListener(mouseWheelListener); + break; + } + } + } + + private void addGridColumnListener(GridColumn column, int width) { + if (gridColumnMouseHandler != null) { + gridColumnMouseHandler.setLimit(width); + return; + } + removeGridColumnListener(column); + gridColumnMouseHandler = new GridLimitColumnMouseHandler(column, width); + column.addMouseListener(gridColumnMouseHandler); + column.addMouseMotionListener(gridColumnMouseHandler); + } + + + private void addGridRowListener(GridRow row, int height) { + if (gridRowMouseHandler != null) { + gridRowMouseHandler.setLimit(height); + return; + } + removeGridRowListener(row); + gridRowMouseHandler = new GridLimitRowMouseHandler(row, height); + row.addMouseMotionListener(gridRowMouseHandler); + row.addMouseListener(gridRowMouseHandler); + } + + + private void addMouseListener(Grid grid, int width, int height, int actualWidth, int actualHeight) { + if (adaptiveGridListener != null) { + adaptiveGridListener.resetBoundInfo(width, height, actualWidth, actualHeight); + return; + } + removeGridListener(grid); + adaptiveGridListener = new AdaptiveGridListener(grid, width, height, actualWidth, actualHeight); + grid.addMouseMotionListener(adaptiveGridListener); + grid.addMouseListener(adaptiveGridListener); + grid.addMouseWheelListener(adaptiveGridListener); + } + +} diff --git a/designer-realize/src/main/java/com/fr/design/mainframe/ElementCasePane.java b/designer-realize/src/main/java/com/fr/design/mainframe/ElementCasePane.java index 30f7b1546..befe413f9 100644 --- a/designer-realize/src/main/java/com/fr/design/mainframe/ElementCasePane.java +++ b/designer-realize/src/main/java/com/fr/design/mainframe/ElementCasePane.java @@ -67,6 +67,7 @@ import com.fr.design.constants.UIConstants; import com.fr.design.designer.EditingState; import com.fr.design.designer.TargetComponent; import com.fr.design.file.HistoryTemplateListPane; +import com.fr.design.fit.DesignerUIModeConfig; import com.fr.design.fun.ElementUIProvider; import com.fr.design.gui.ibutton.UIButton; import com.fr.design.gui.imenu.UIPopupMenu; @@ -225,7 +226,7 @@ public abstract class ElementCasePane extends Tar //todo 直接修改分辨率 if (this.resolution == 0) { - this.resolution = ScreenResolution.getScreenResolution(); + this.resolution = DesignerUIModeConfig.getInstance().getScreenResolution(); } this.initGridComponent(); @@ -351,10 +352,10 @@ public abstract class ElementCasePane extends Tar this.grid = new Grid(this.resolution); } if (this.gridColumn == null) { - this.gridColumn = new GridColumn(); + this.gridColumn = new GridColumn(this.resolution); } if (this.gridRow == null) { - this.gridRow = new GridRow(); + this.gridRow = new GridRow(this.resolution); } if (this.gridCorner == null) { this.gridCorner = new GridCorner(); @@ -1375,7 +1376,7 @@ public abstract class ElementCasePane extends Tar protected Selection selection; protected int verticalValue = 0; protected int horizontalValue = 0; - protected int resolution = ScreenResolution.getScreenResolution(); + protected int resolution = DesignerUIModeConfig.getInstance().getScreenResolution(); protected ElementCaseEditingState(Selection selection, int verticalValue, int horizontalValue, int resolution) { try { diff --git a/designer-realize/src/main/java/com/fr/design/mainframe/JWorkBook.java b/designer-realize/src/main/java/com/fr/design/mainframe/JWorkBook.java index cbe2af93e..fa1fe93cd 100644 --- a/designer-realize/src/main/java/com/fr/design/mainframe/JWorkBook.java +++ b/designer-realize/src/main/java/com/fr/design/mainframe/JWorkBook.java @@ -55,8 +55,10 @@ import com.fr.design.module.DesignModuleFactory; import com.fr.design.parameter.ParameterDefinitePane; import com.fr.design.parameter.ParameterInputPane; import com.fr.design.preview.MobilePreview; +import com.fr.design.preview.PagePlusPreview; import com.fr.design.preview.PagePreview; import com.fr.design.preview.ViewPreview; +import com.fr.design.preview.WriteEnhancePreview; import com.fr.design.preview.WritePreview; import com.fr.design.report.fit.menupane.ReportFitAttrAction; import com.fr.design.roleAuthority.ReportAndFSManagePane; @@ -917,11 +919,7 @@ public class JWorkBook extends JTemplate { public PreviewProvider[] supportPreview() { PreviewProvider[] templatePreviews = super.supportPreview(); return ArrayUtils.addAll(new PreviewProvider[]{ -<<<<<<< HEAD - new PagePreview(), new WritePreview(), new ViewPreview(), new MobilePreview(), new PagePlusPreview() -======= - new PagePreview(), new WritePreview(), new ViewPreview(), new WriteEnhancePreview(), new MobilePreview() ->>>>>>> 858f2c1596f623fad4706025ed356d8ff22ccc22 + new PagePreview(), new WritePreview(), new ViewPreview(), new WriteEnhancePreview(), new MobilePreview(), new PagePlusPreview() }, templatePreviews); } @@ -1197,6 +1195,7 @@ public class JWorkBook extends JTemplate { protected void addChooseFILEFilter(FILEChooserPane fileChooser) { String appName = ProductConstants.APP_NAME; fileChooser.addChooseFILEFilter(new ChooseFileFilter(FileExtension.CPT, appName + Toolkit.i18nText("Fine-Design_Report_Template_File"))); + fileChooser.addChooseFILEFilter(new ChooseFileFilter(FileExtension.CPTX, appName + Toolkit.i18nText("Fine-Design_Report_Template_File"))); addExtraChooseFILEFilter(fileChooser); } diff --git a/designer-realize/src/main/java/com/fr/design/mainframe/WorkBookModelAdapter.java b/designer-realize/src/main/java/com/fr/design/mainframe/WorkBookModelAdapter.java index 87391a5a0..1a4f20c3d 100644 --- a/designer-realize/src/main/java/com/fr/design/mainframe/WorkBookModelAdapter.java +++ b/designer-realize/src/main/java/com/fr/design/mainframe/WorkBookModelAdapter.java @@ -18,12 +18,9 @@ import com.fr.stable.StringUtils; import com.fr.stable.bridge.StableFactory; import com.fr.stable.js.WidgetName; -import com.fr.util.ParameterApplyHelper; import java.util.ArrayList; -import java.util.HashMap; import java.util.Iterator; import java.util.List; -import java.util.Map; /** * @author zhou @@ -75,18 +72,6 @@ public class WorkBookModelAdapter extends DesignModelAdapter map = new HashMap<>(); - addGlobalParameters(map); - updateTableDataParameters(oldName, tdName, tableData, map, null); - addReportParameters(map); - parameters = map.values().toArray(new Parameter[0]); - jTemplate.updateReportParameterAttr(); - jTemplate.populateReportParameterAttr(); - - } - /** * 控件改变. */ @@ -122,27 +107,9 @@ public class WorkBookModelAdapter extends DesignModelAdapter map = new HashMap<>(); - addGlobalParameters(map); - - addTableDataParameters(map, null); - - addReportParameters(map); - - return map.values().toArray(new Parameter[0]); - } - - private void addReportParameters(Map map) { - // 添加模板参数 - ReportParameterAttr parameterAttr = this.getBook().getReportParameterAttr(); - if (parameterAttr != null) { - ParameterApplyHelper.addPara2Map(map, parameterAttr.getParameters()); - } + return this.getBook().getParameters(); } - - - /** * 返回控件的名字 * diff --git a/designer-realize/src/main/java/com/fr/design/mainframe/app/DesignerAppActivator.java b/designer-realize/src/main/java/com/fr/design/mainframe/app/DesignerAppActivator.java index ec425847d..23ddcbe8a 100644 --- a/designer-realize/src/main/java/com/fr/design/mainframe/app/DesignerAppActivator.java +++ b/designer-realize/src/main/java/com/fr/design/mainframe/app/DesignerAppActivator.java @@ -4,6 +4,7 @@ import com.fr.design.mainframe.App; import com.fr.design.mainframe.JTemplateFactory; import com.fr.module.Activator; import com.fr.module.extension.Prepare; +import com.fr.nx.app.designer.CptxApp; import java.util.List; @@ -33,7 +34,7 @@ public class DesignerAppActivator extends Activator implements Prepare { @Override public void prepare() { - addMutable(App.KEY, new CptApp(), new FormApp(), new XlsApp(), new XlsxApp()); + addMutable(App.KEY, new CptApp(), new CptxApp(), new FormApp(), new XlsApp(), new XlsxApp()); } } diff --git a/designer-realize/src/main/java/com/fr/design/mainframe/cell/settingpane/CellExpandExtraAttrPane.java b/designer-realize/src/main/java/com/fr/design/mainframe/cell/settingpane/CellExpandExtraAttrPane.java index 13c288f92..e9af09a4e 100644 --- a/designer-realize/src/main/java/com/fr/design/mainframe/cell/settingpane/CellExpandExtraAttrPane.java +++ b/designer-realize/src/main/java/com/fr/design/mainframe/cell/settingpane/CellExpandExtraAttrPane.java @@ -7,6 +7,7 @@ import com.fr.design.layout.TableLayout; import com.fr.design.layout.TableLayoutHelper; import com.fr.design.ui.util.UIUtil; import com.fr.event.EventDispatcher; +import com.fr.nx.app.designer.cell.CellTreeAttrPanelProvider; import com.fr.plugin.context.PluginContext; import com.fr.plugin.injectable.PluginModule; import com.fr.plugin.manage.PluginFilter; @@ -19,7 +20,10 @@ import java.awt.BorderLayout; import java.awt.Component; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collections; +import java.util.LinkedHashSet; import java.util.List; +import java.util.Optional; import java.util.Set; import static com.fr.plugin.observer.PluginEventType.AfterRun; @@ -114,14 +118,14 @@ public class CellExpandExtraAttrPane extends JPanel { extras = new ArrayList<>(); } extras.clear(); - Set attrProviders = ExtraDesignClassManager.getInstance().getArray(CellExpandAttrPanelProvider.MARK_STRING); - if (attrProviders != null) { - for (CellExpandAttrPanelProvider attrProvider : attrProviders) { - if (attrProvider.isDisplayable()) { - BasicBeanPane extra = attrProvider.createPanel(); - if (extra != null) { - extras.add(extra); - } + Set attrProviders = new LinkedHashSet<>(Collections.singletonList(new CellTreeAttrPanelProvider())); + Optional.>of(ExtraDesignClassManager.getInstance().getArray(CellExpandAttrPanelProvider.MARK_STRING)) + .ifPresent(attrProviders::addAll); + for (CellExpandAttrPanelProvider attrProvider : attrProviders) { + if (attrProvider.isDisplayable()) { + BasicBeanPane extra = attrProvider.createPanel(); + if (extra != null) { + extras.add(extra); } } } diff --git a/designer-realize/src/main/java/com/fr/design/mainframe/form/FormElementCaseDesigner.java b/designer-realize/src/main/java/com/fr/design/mainframe/form/FormElementCaseDesigner.java index 45c2fea61..dea5145ce 100644 --- a/designer-realize/src/main/java/com/fr/design/mainframe/form/FormElementCaseDesigner.java +++ b/designer-realize/src/main/java/com/fr/design/mainframe/form/FormElementCaseDesigner.java @@ -13,6 +13,7 @@ import com.fr.design.designer.EditingState; import com.fr.design.designer.TargetComponent; import com.fr.design.event.TargetModifiedEvent; import com.fr.design.event.TargetModifiedListener; +import com.fr.design.fit.DesignerUIModeConfig; import com.fr.design.gui.frpane.HyperlinkGroupPane; import com.fr.design.layout.FRGUIPaneFactory; import com.fr.design.mainframe.AuthorityEditPane; @@ -137,6 +138,9 @@ public class FormElementCaseDesigner g.setColor(Color.WHITE); g.fillRect(0, 0, size.width, size.height); g.setColor(oldColor); + if (DesignerUIModeConfig.getInstance().newUIMode()) { + g.translate(-elementCasePane.getGridCorner().getWidth(), -elementCasePane.getGridCorner().getHeight()); + } this.elementCasePane.paintComponents(g); diff --git a/designer-realize/src/main/java/com/fr/design/write/submit/SmartInsertDBManipulationPane.java b/designer-realize/src/main/java/com/fr/design/write/submit/SmartInsertDBManipulationPane.java index d306bb179..3fe126879 100644 --- a/designer-realize/src/main/java/com/fr/design/write/submit/SmartInsertDBManipulationPane.java +++ b/designer-realize/src/main/java/com/fr/design/write/submit/SmartInsertDBManipulationPane.java @@ -310,9 +310,9 @@ public class SmartInsertDBManipulationPane extends DBManipulationPane { // 单元格组要记录下之前的选中情况 private CellSelection oriCellSelection = null; - private List newAdd = new ArrayList(); + private List newAdd = new ArrayList<>(); - private List oldAdd = new ArrayList(); + private List oldAdd = new ArrayList<>(); public SmartJTablePane4DB(KeyColumnTableModel model, ElementCasePane actionReportPane) { this(model, actionReportPane, false); @@ -433,7 +433,8 @@ public class SmartInsertDBManipulationPane extends DBManipulationPane { if (!allColumnRow.contains(columnRow.toString())) { add.addColumnRow(columnRow); } - + // 重新更换区域框选单元格后 清理历史框选 + oldAdd.clear(); } if (add.getSize() > 0) { @@ -459,10 +460,11 @@ public class SmartInsertDBManipulationPane extends DBManipulationPane { TemplateCellElement cellElement = ePane.getEditingElementCase().getTemplateCellElement(c + i, r + j); if (cellElement != null && ((i + c) != 0 || (r + j) != 0)) { String value = cellElement.toString(); - if (!newAdd.contains(value) && !allColumnRow.contains(value)) { - add.addColumnRow(ColumnRow.valueOf(value)); + ColumnRow columnRow = ColumnRow.valueOf(value); + if (!newAdd.contains(columnRow) && !allColumnRow.contains(value)) { + add.addColumnRow(columnRow); } - newAdd.add(value); + newAdd.add(columnRow); } if (cellElement == null) { @@ -470,16 +472,14 @@ public class SmartInsertDBManipulationPane extends DBManipulationPane { if (!allColumnRow.contains(columnRow.toString())) { add.addColumnRow(columnRow); } - newAdd.add(columnRow.toString()); + newAdd.add(columnRow); } } } - int oldSize = oldAdd.size(); - int newSize = newAdd.size(); - if (oldSize > newSize && oldAdd.containsAll(newAdd)) { - int diff = oldSize - newSize; - newValue.splice(newValue.getSize() - diff, diff); - } + // 计算出前后两次选中的差值 + oldAdd.removeAll(newAdd); + // 移除差值部分 + newValue.removeAll(oldAdd); oldAdd.clear(); oldAdd.addAll(newAdd); } diff --git a/designer-realize/src/main/java/com/fr/grid/AbstractGridHeaderMouseHandler.java b/designer-realize/src/main/java/com/fr/grid/AbstractGridHeaderMouseHandler.java index 349503bd7..b04e4ad1e 100644 --- a/designer-realize/src/main/java/com/fr/grid/AbstractGridHeaderMouseHandler.java +++ b/designer-realize/src/main/java/com/fr/grid/AbstractGridHeaderMouseHandler.java @@ -2,13 +2,12 @@ package com.fr.grid; import com.fr.base.BaseUtils; import com.fr.base.DynamicUnitList; -import com.fr.base.ScreenResolution; import com.fr.base.vcs.DesignerMode; import com.fr.design.DesignerEnvManager; +import com.fr.design.fit.DesignerUIModeConfig; import com.fr.design.fun.ReportLengthUNITProvider; import com.fr.design.gui.imenu.UIPopupMenu; import com.fr.design.mainframe.ElementCasePane; -import com.fr.design.unit.UnitConvertUtil; import com.fr.design.utils.gui.GUICoreUtils; import com.fr.grid.selection.CellSelection; import com.fr.grid.selection.FloatSelection; @@ -39,7 +38,7 @@ import java.lang.reflect.Method; public abstract class AbstractGridHeaderMouseHandler extends MouseInputAdapter { protected static final int SEPARATOR_GAP = 5; - private GridHeader gHeader; + protected GridHeader gHeader; private int dragType = GridUtils.DRAG_NONE; //james 是否为选定多行的drag private boolean isMultiSelectDragPermited = false; @@ -51,7 +50,7 @@ public abstract class AbstractGridHeaderMouseHandler extends MouseInputAdapter { private int dragIndex = 0; private JToolTip tip = null; private JWindow tipWindow = null; - protected int resolution = ScreenResolution.getScreenResolution(); + protected int resolution = DesignerUIModeConfig.getInstance().getScreenResolution(); public AbstractGridHeaderMouseHandler(GridHeader gHeader) { this.gHeader = gHeader; @@ -307,7 +306,7 @@ public abstract class AbstractGridHeaderMouseHandler extends MouseInputAdapter { // int resolution = ScreenResolution.getScreenResolution(); FU ulen = FU.valueOfPix((int) doubleValue, resolution); FU tulen = FU.valueOfPix((int) totalDoubleValue, resolution); - ReportLengthUNITProvider lengthUNIT = UnitConvertUtil.parseLengthUNIT(unitType); + ReportLengthUNITProvider lengthUNIT = DesignerUIModeConfig.getInstance().parseLengthUNIT(unitType); String unit = lengthUNIT.unitText(); double len = lengthUNIT.unit2Value4Scale(ulen); double tlen = lengthUNIT.unit2Value4Scale(tulen); diff --git a/designer-realize/src/main/java/com/fr/grid/DefaultGridUIProcessor.java b/designer-realize/src/main/java/com/fr/grid/DefaultGridUIProcessor.java index 65b6aa456..0b4c900e3 100644 --- a/designer-realize/src/main/java/com/fr/grid/DefaultGridUIProcessor.java +++ b/designer-realize/src/main/java/com/fr/grid/DefaultGridUIProcessor.java @@ -1,16 +1,24 @@ package com.fr.grid; +import com.fr.design.fit.DesignerUIModeConfig; +import com.fr.design.fit.grid.NewFormDesignerGridUI; import com.fr.design.fun.impl.AbstractGridUIProcessor; +import com.fr.design.mainframe.FormDesigner; +import com.fr.design.mainframe.WidgetPropertyPane; import javax.swing.plaf.ComponentUI; /** * Created by Administrator on 2016/6/28/0028. */ -public class DefaultGridUIProcessor extends AbstractGridUIProcessor{ +public class DefaultGridUIProcessor extends AbstractGridUIProcessor { @Override public ComponentUI appearanceForGrid(int resolution) { + if (DesignerUIModeConfig.getInstance().newUIMode()) { + FormDesigner designer = WidgetPropertyPane.getInstance().getEditingFormDesigner(); + return new NewFormDesignerGridUI(designer, resolution); + } return new GridUI(resolution); } diff --git a/designer-realize/src/main/java/com/fr/grid/GridColumn.java b/designer-realize/src/main/java/com/fr/grid/GridColumn.java index 8ba6484bc..eed4136d4 100644 --- a/designer-realize/src/main/java/com/fr/grid/GridColumn.java +++ b/designer-realize/src/main/java/com/fr/grid/GridColumn.java @@ -6,14 +6,10 @@ package com.fr.grid; import java.awt.Dimension; import com.fr.base.GraphHelper; -import com.fr.base.ScreenResolution; -import com.fr.design.ExtraDesignClassManager; -import com.fr.design.fun.GridUIProcessor; +import com.fr.design.fit.DesignerUIModeConfig; import com.fr.design.mainframe.ElementCasePane; import com.fr.stable.StableUtils; -import javax.swing.plaf.ComponentUI; - /** * GridColumn used to paint and edit grid column. * @@ -22,20 +18,23 @@ import javax.swing.plaf.ComponentUI; */ public class GridColumn extends GridHeader { - public int resolution = ScreenResolution.getScreenResolution(); private GridColumnMouseHandler gridColumnMouseHandler; + public GridColumn(int resolution) { + super(resolution); + } + @Override protected void initByConstructor() { - resolution = ScreenResolution.getScreenResolution(); - this.setResolution(resolution); gridColumnMouseHandler = new GridColumnMouseHandler(this); this.addMouseListener(gridColumnMouseHandler); this.addMouseMotionListener(gridColumnMouseHandler); this.updateUI(); } + + @Override public String getDisplay(int index) { return StableUtils.convertIntToABC(index + 1); @@ -48,26 +47,16 @@ public class GridColumn extends GridHeader { gridColumnMouseHandler = new GridColumnMouseHandler(this); this.addMouseListener(gridColumnMouseHandler); this.addMouseMotionListener(gridColumnMouseHandler); -// gridColumnMouseHandler.setResolution(resolution); this.setUI(new GridColumnUI(resolution)); } - public void setResolution(int resolution) { - this.resolution = resolution; - } - - @Override - public int getResolution() { - return this.resolution; - } - /** * Gets the preferred size. */ @Override public Dimension getPreferredSize() { ElementCasePane reportPane = this.getElementCasePane(); - float time = (float)reportPane.getResolution()/ ScreenResolution.getScreenResolution(); + float time = (float)reportPane.getResolution()/ DesignerUIModeConfig.getInstance().getScreenResolution(); if (!reportPane.isColumnHeaderVisible()) { return new Dimension(0, 0); } diff --git a/designer-realize/src/main/java/com/fr/grid/GridColumnMouseHandler.java b/designer-realize/src/main/java/com/fr/grid/GridColumnMouseHandler.java index 09ad45d32..bc5b54c90 100644 --- a/designer-realize/src/main/java/com/fr/grid/GridColumnMouseHandler.java +++ b/designer-realize/src/main/java/com/fr/grid/GridColumnMouseHandler.java @@ -7,9 +7,7 @@ import java.awt.Toolkit; import java.awt.event.MouseEvent; import javax.swing.SwingUtilities; - import com.fr.base.DynamicUnitList; -import com.fr.base.ScreenResolution; import com.fr.design.gui.imenu.UIPopupMenu; import com.fr.design.mainframe.ElementCasePane; import com.fr.grid.selection.CellSelection; @@ -23,8 +21,6 @@ import com.fr.stable.ColumnRow; */ public class GridColumnMouseHandler extends AbstractGridHeaderMouseHandler { - private int resolution; - public GridColumnMouseHandler(GridColumn gridColumn) { super(gridColumn); this.resolution = gridColumn.resolution; diff --git a/designer-realize/src/main/java/com/fr/grid/GridColumnUI.java b/designer-realize/src/main/java/com/fr/grid/GridColumnUI.java index d3e90308b..088c8f220 100644 --- a/designer-realize/src/main/java/com/fr/grid/GridColumnUI.java +++ b/designer-realize/src/main/java/com/fr/grid/GridColumnUI.java @@ -16,6 +16,7 @@ import com.fr.base.ScreenResolution; import com.fr.base.vcs.DesignerMode; import com.fr.cache.list.IntList; import com.fr.design.constants.UIConstants; +import com.fr.design.fit.DesignerUIModeConfig; import com.fr.design.mainframe.ElementCasePane; import com.fr.design.roleAuthority.ReportAndFSManagePane; import com.fr.grid.selection.Selection; @@ -33,7 +34,7 @@ public class GridColumnUI extends ComponentUI { public GridColumnUI(int resolution){ if (resolution == 0){ - resolution = ScreenResolution.getScreenResolution(); + resolution = DesignerUIModeConfig.getInstance().getScreenResolution(); } this.resolution = resolution; } @@ -49,7 +50,7 @@ public class GridColumnUI extends ComponentUI { Grid gird = reportPane.getGrid(); // size Dimension size = gridColumn.getSize(); - float time = (float)resolution/ScreenResolution.getScreenResolution(); + float time = (float)resolution/DesignerUIModeConfig.getInstance().getScreenResolution(); g2d.setFont(gridColumn.getFont().deriveFont(gridColumn.getFont().getSize2D() * time)); ElementCase elementCase = reportPane.getEditingElementCase(); @@ -161,7 +162,7 @@ public class GridColumnUI extends ComponentUI { columnContent += "(FR)"; } } - float time = (float)resolution/ScreenResolution.getScreenResolution(); + float time = (float)resolution/DesignerUIModeConfig.getInstance().getScreenResolution(); double stringWidth = gridColumn.getFont().getStringBounds(columnContent, fontRenderContext).getWidth() * time; if (stringWidth > tmpIncreaseWidth) { diff --git a/designer-realize/src/main/java/com/fr/grid/GridCorner.java b/designer-realize/src/main/java/com/fr/grid/GridCorner.java index f2dd43d5e..4d1d393b7 100644 --- a/designer-realize/src/main/java/com/fr/grid/GridCorner.java +++ b/designer-realize/src/main/java/com/fr/grid/GridCorner.java @@ -6,6 +6,7 @@ package com.fr.grid; import com.fr.base.GraphHelper; import com.fr.base.ScreenResolution; import com.fr.design.constants.UIConstants; +import com.fr.design.fit.DesignerUIModeConfig; import com.fr.design.mainframe.ElementCasePane; import javax.swing.event.MouseInputListener; @@ -35,7 +36,7 @@ public class GridCorner extends BaseGridComponent { Graphics2D g2d = (Graphics2D) g; ElementCasePane reportPane = this.getElementCasePane(); - float time = (float) reportPane.getResolution() / ScreenResolution.getScreenResolution(); + float time = (float) reportPane.getResolution() / DesignerUIModeConfig.getInstance().getScreenResolution(); //size Dimension size = this.getSize(); Rectangle2D rect2D = new Rectangle2D.Double(0, 0, size.getWidth(), size.getHeight()); diff --git a/designer-realize/src/main/java/com/fr/grid/GridHeader.java b/designer-realize/src/main/java/com/fr/grid/GridHeader.java index 9a4a1b949..ec974b197 100644 --- a/designer-realize/src/main/java/com/fr/grid/GridHeader.java +++ b/designer-realize/src/main/java/com/fr/grid/GridHeader.java @@ -15,7 +15,8 @@ public abstract class GridHeader extends BaseGridComponent { protected int resolution; - public GridHeader() { + public GridHeader(int resolution) { + this.resolution = resolution; //清除所有的Key Action. this.getInputMap().clear(); this.getActionMap().clear(); diff --git a/designer-realize/src/main/java/com/fr/grid/GridMouseAdapter.java b/designer-realize/src/main/java/com/fr/grid/GridMouseAdapter.java index b0f91a902..060bc2ace 100644 --- a/designer-realize/src/main/java/com/fr/grid/GridMouseAdapter.java +++ b/designer-realize/src/main/java/com/fr/grid/GridMouseAdapter.java @@ -6,6 +6,7 @@ import com.fr.base.ScreenResolution; import com.fr.base.vcs.DesignerMode; import com.fr.common.inputevent.InputEventBaseOnOS; import com.fr.design.constants.UIConstants; +import com.fr.design.fit.DesignerUIModeConfig; import com.fr.design.mainframe.DesignerContext; import com.fr.design.mainframe.EastRegionContainerPane; import com.fr.design.mainframe.ElementCasePane; @@ -73,7 +74,7 @@ public class GridMouseAdapter implements MouseListener, MouseWheelListener, Mous private int ECBlockGap = 40; - private int resolution = ScreenResolution.getScreenResolution(); + private int resolution = DesignerUIModeConfig.getInstance().getScreenResolution(); protected GridMouseAdapter(Grid grid) { this.grid = grid; diff --git a/designer-realize/src/main/java/com/fr/grid/GridRow.java b/designer-realize/src/main/java/com/fr/grid/GridRow.java index 7dbb4af3a..a85c3c398 100644 --- a/designer-realize/src/main/java/com/fr/grid/GridRow.java +++ b/designer-realize/src/main/java/com/fr/grid/GridRow.java @@ -8,6 +8,7 @@ import java.awt.Dimension; import com.fr.base.GraphHelper; import com.fr.base.ScreenResolution; import com.fr.design.ExtraDesignClassManager; +import com.fr.design.fit.DesignerUIModeConfig; import com.fr.design.fun.GridUIProcessor; import com.fr.design.mainframe.ElementCasePane; @@ -22,13 +23,14 @@ import javax.swing.plaf.ComponentUI; public class GridRow extends GridHeader { private static final int MAX = 4; - private int resolution = ScreenResolution.getScreenResolution(); private GridRowMouseHandler gridRowMouseHandler; + public GridRow(int resolution) { + super(resolution); + } + @Override protected void initByConstructor() { - resolution = ScreenResolution.getScreenResolution(); - this.setResolution(resolution); gridRowMouseHandler = new GridRowMouseHandler(this); this.addMouseListener(gridRowMouseHandler); this.addMouseMotionListener(gridRowMouseHandler); @@ -50,14 +52,6 @@ public class GridRow extends GridHeader { this.setUI(new GridRowUI(resolution)); } - public void setResolution(int resolution) { - this.resolution = resolution; - } - - public int getResolution() { - return this.resolution; - } - /** * Gets the preferred size. @@ -65,7 +59,7 @@ public class GridRow extends GridHeader { @Override public Dimension getPreferredSize() { ElementCasePane reportPane = this.getElementCasePane(); - float time = (float)reportPane.getResolution()/ ScreenResolution.getScreenResolution(); + float time = (float)reportPane.getResolution()/ DesignerUIModeConfig.getInstance().getScreenResolution(); if (!(reportPane.isRowHeaderVisible())) { return new Dimension(0, 0); } diff --git a/designer-realize/src/main/java/com/fr/grid/GridRowUI.java b/designer-realize/src/main/java/com/fr/grid/GridRowUI.java index a0245a3d8..6c2fed6d0 100644 --- a/designer-realize/src/main/java/com/fr/grid/GridRowUI.java +++ b/designer-realize/src/main/java/com/fr/grid/GridRowUI.java @@ -7,6 +7,8 @@ import java.awt.geom.Rectangle2D; import javax.swing.JComponent; import javax.swing.UIManager; import javax.swing.plaf.ComponentUI; + +import com.fr.design.fit.DesignerUIModeConfig; import com.fr.stable.AssistUtils; import com.fr.base.BaseUtils; import com.fr.base.DynamicUnitList; @@ -32,7 +34,7 @@ public class GridRowUI extends ComponentUI { GridRowUI(int resolution){ if (resolution == 0){ - resolution = ScreenResolution.getScreenResolution(); + resolution = DesignerUIModeConfig.getInstance().getScreenResolution(); } this.resolution = resolution; } @@ -47,7 +49,7 @@ public class GridRowUI extends ComponentUI { ElementCasePane reportPane = gridRow.getElementCasePane(); // size Dimension size = gridRow.getSize(); - float time = (float)resolution/ScreenResolution.getScreenResolution(); + float time = (float)resolution/DesignerUIModeConfig.getInstance().getScreenResolution(); g2d.setFont(gridRow.getFont().deriveFont(gridRow.getFont().getSize2D() * time)); ElementCase elementCase = reportPane.getEditingElementCase(); @@ -156,7 +158,7 @@ public class GridRowUI extends ComponentUI { , ElementCase elementCase, Dimension size, double tmpHeight1) { // FontMetrics FontRenderContext fontRenderContext = g2d.getFontRenderContext(); - float time = (float)resolution/ScreenResolution.getScreenResolution(); + float time = (float)resolution/DesignerUIModeConfig.getInstance().getScreenResolution(); float fmAscent = GraphHelper.getFontMetrics(gridRow.getFont()).getAscent() * time; double stringWidth = gridRow.getFont().getStringBounds(paintText, fontRenderContext).getWidth() * time; double stringHeight = gridRow.getFont().getStringBounds(paintText, fontRenderContext).getHeight() * time; diff --git a/designer-realize/src/main/java/com/fr/grid/GridUtils.java b/designer-realize/src/main/java/com/fr/grid/GridUtils.java index 80e6f3ff5..31ceacd7a 100644 --- a/designer-realize/src/main/java/com/fr/grid/GridUtils.java +++ b/designer-realize/src/main/java/com/fr/grid/GridUtils.java @@ -5,6 +5,7 @@ import com.fr.base.ScreenResolution; import com.fr.design.cell.clipboard.CellElementsClip; import com.fr.design.cell.clipboard.ElementsTransferable; import com.fr.design.cell.clipboard.FloatElementsClip; +import com.fr.design.fit.DesignerUIModeConfig; import com.fr.design.mainframe.ElementCasePane; import com.fr.design.utils.gui.GUICoreUtils; import com.fr.general.ComparatorUtils; @@ -124,7 +125,7 @@ public class GridUtils { */ public static double[] caculateFloatElementLocations(FloatElement floatElement, DynamicUnitList columnWidthList, DynamicUnitList rowHeightList, int verticalValue, int horizentalValue) { - int resolution = ScreenResolution.getScreenResolution(); + int resolution = DesignerUIModeConfig.getInstance().getScreenResolution(); double floatX = columnWidthList.getRangeValue(horizentalValue, 0).toPixD(resolution) + floatElement.getLeftDistance().toPixD(resolution); double floatY = rowHeightList.getRangeValue(verticalValue, 0).toPixD(resolution) + floatElement.getTopDistance().toPixD(resolution); @@ -177,7 +178,7 @@ public class GridUtils { private static int cc_selected_column_or_row(double mouseEvtPosition, int beginValue, int value, DynamicUnitList sizeList) { double tmpIntIndex = 0; int selectedCellIndex = 0; - int resolution = ScreenResolution.getScreenResolution(); + int resolution = DesignerUIModeConfig.getInstance().getScreenResolution(); if (mouseEvtPosition < 0) { selectedCellIndex = value; for (; true; selectedCellIndex--) { diff --git a/designer-realize/src/main/java/com/fr/nx/app/designer/CptxApp.java b/designer-realize/src/main/java/com/fr/nx/app/designer/CptxApp.java new file mode 100644 index 000000000..9186a6734 --- /dev/null +++ b/designer-realize/src/main/java/com/fr/nx/app/designer/CptxApp.java @@ -0,0 +1,53 @@ +package com.fr.nx.app.designer; + +import com.fr.base.extension.FileExtension; +import com.fr.design.mainframe.AbstractAppProvider; +import com.fr.design.mainframe.JTemplate; +import com.fr.file.FILE; +import com.fr.main.impl.WorkBook; +import com.fr.nx.app.designer.utils.DesignerCptxFileUtils; + +/** + * 支持设计器打开cptx文件 + */ +public class CptxApp extends AbstractAppProvider { + + public CptxApp() { + StartupAssist.initDesignModule(); + } + + @Override + public String[] defaultExtensions() { + return new String[]{FileExtension.CPTX.getExtension()}; + } + + @Override + public void process() { + super.process(); + StartupAssist.reloadTemplate(); + } + + /** + * 该方法只是为了读到cptx中的cpt + * + * @param tplFile file + * @return template + */ + @Override + public JTemplate openTemplate(FILE tplFile) { + WorkBook workBook = DesignerCptxFileUtils.getWorkBook(tplFile); + if (workBook == null) { + workBook = new WorkBook(); + } + return new JStreamBook(workBook, tplFile); + } + + @Override + public WorkBook asIOFile(FILE file) { + WorkBook workBook = DesignerCptxFileUtils.getWorkBook(file); + if (workBook == null) { + workBook = new WorkBook(); + } + return workBook; + } +} diff --git a/designer-realize/src/main/java/com/fr/nx/app/designer/JStreamBook.java b/designer-realize/src/main/java/com/fr/nx/app/designer/JStreamBook.java new file mode 100644 index 000000000..2fa748f95 --- /dev/null +++ b/designer-realize/src/main/java/com/fr/nx/app/designer/JStreamBook.java @@ -0,0 +1,117 @@ +package com.fr.nx.app.designer; + +import com.fr.base.extension.FileExtension; +import com.fr.design.actions.file.export.CSVExportAction; +import com.fr.design.actions.file.export.PDFExportAction; +import com.fr.design.actions.file.export.SVGExportAction; +import com.fr.design.actions.file.export.TextExportAction; +import com.fr.design.actions.file.export.WordExportAction; +import com.fr.design.mainframe.JWorkBook; +import com.fr.design.menu.MenuDef; +import com.fr.design.menu.ShortCut; +import com.fr.file.FILE; +import com.fr.general.ComparatorUtils; +import com.fr.locale.InterProviderFactory; +import com.fr.log.FineLoggerFactory; +import com.fr.main.impl.WorkBook; +import com.fr.nx.app.designer.utils.DesignerCptxFileUtils; +import com.fr.nx.cptx.entry.metadata.CptxMetadata; +import com.fr.nx.app.designer.menu.CalculateAttrAction; +import com.fr.nx.app.designer.toolbar.TemplateTransformer; +import com.fr.nx.app.designer.toolbar.TransformResult; +import com.fr.nx.app.designer.toolbar.TransformResultInfo; +import com.fr.stable.StringUtils; +import com.fr.stable.project.ProjectConstants; +import com.fr.third.jodd.util.ArraysUtil; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.OutputStream; + +public class JStreamBook extends JWorkBook { + + + public JStreamBook(WorkBook workBook, FILE file) { + super(workBook, file); + } + + @Override + protected boolean saveFile() { + boolean saved; + TransformResultInfo resultInfo = TemplateTransformer.compileCPTX(getTarget(), getEditingFILE()); + saved = resultInfo.isSaved(); + this.setSaved(saved); + if (saved) { + this.fireJTemplateSaved(); + } + return saved; + } + + @Override + protected boolean saveToNewFile(String oldName) { + String path = this.getEditingFILE().getPath(); + try { + if (!path.startsWith(ProjectConstants.REPORTLETS_NAME)) { + File file = new File(path); + if (file.getParentFile() != null && !file.getParentFile().exists()) { + file.getParentFile().mkdirs(); + } + file.createNewFile(); + OutputStream outputStream = new FileOutputStream(file); + this.getTarget().export(outputStream); + return true; + }else { + TransformResult result; + if (FileExtension.CPT.matchExtension(path)) { + result = TemplateTransformer.TO_CPT.transform(this); + } else { + result = TemplateTransformer.TO_CPTX.transform(this); + } + return ComparatorUtils.equals(TransformResult.SUCCESS, result); + } + } catch (Exception e) { + FineLoggerFactory.getLogger().error(e.getMessage(), e); + } + return false; + } + + + /** + * 保存文件的后缀名 + * + * @return 后缀的字符串 + */ + @Override + public String suffix() { + return ".cptx"; + } + + @Override + protected void addShortCut(MenuDef exportMenuDef, MenuDef excelExportMenuDef) { + exportMenuDef.addShortCut(excelExportMenuDef, new PDFExportAction(this), new WordExportAction(this), new SVGExportAction(this), + new CSVExportAction(this), new TextExportAction(this)); + } + + + @Override + public String getPath() { + return getEditingFILE().getPath() + getSuffix(); + } + + public String getTemplateName() { + return getEditingFILE().getName() + getSuffix(); + } + + private String getSuffix() { + CptxMetadata metadata = DesignerCptxFileUtils.getMetadata(this.getEditingFILE()); + if (metadata != null && metadata.isForceCpt()) { + return InterProviderFactory.getProvider().getLocText("Fine-Plugin_Engine_Compatibility_Mode"); + } + return StringUtils.EMPTY; + } + + @Override + public ShortCut[] shortcut4TemplateMenu() { + return ArraysUtil.insert(super.shortcut4TemplateMenu(), new CalculateAttrAction(this), 5); + } +} diff --git a/designer-realize/src/main/java/com/fr/nx/app/designer/StartupAssist.java b/designer-realize/src/main/java/com/fr/nx/app/designer/StartupAssist.java new file mode 100644 index 000000000..be4d9ddc8 --- /dev/null +++ b/designer-realize/src/main/java/com/fr/nx/app/designer/StartupAssist.java @@ -0,0 +1,75 @@ +package com.fr.nx.app.designer; + +import com.fr.base.Parameter; +import com.fr.base.extension.FileExtension; +import com.fr.common.annotations.Negative; +import com.fr.design.file.HistoryTemplateListCache; +import com.fr.design.mainframe.DesignerContext; +import com.fr.design.mainframe.JTemplate; +import com.fr.design.mainframe.JTemplateFactory; +import com.fr.design.module.DesignModuleFactory; +import com.fr.design.parameter.AbstractParameterReader; +import com.fr.design.ui.util.UIUtil; +import com.fr.log.FineLoggerFactory; +import com.fr.main.impl.WorkBook; +import com.fr.nx.cptx.CptxIOManager; +import com.fr.nx.cptx.io.handle.CptxTemplateHandle; + +/** + * 保证设计器默认 cptx 启动正常 + * + * @author yaohwu + * created by yaohwu at 2020/6/5 16:28 + */ +public class StartupAssist { + + public static void initDesignModule() { + DesignModuleFactory.registerParameterReader(new AbstractParameterReader() { + @Override + public Parameter[] readParameterFromPath(String tplPath) { + if (accept(tplPath, FileExtension.CPTX.getSuffix())) { + try { + CptxTemplateHandle handle = CptxIOManager.open(tplPath); + WorkBook book = handle.getTemplate().getLegacyWorkBook(); + return book.getParameters(); + } catch (Exception e) { + FineLoggerFactory.getLogger().error(e.getMessage(), e); + return new Parameter[0]; + } + } + return null; + } + }); + } + + private static void reload() { + JTemplate old = HistoryTemplateListCache.getInstance().getCurrentEditingTemplate(); + JTemplate template = JTemplateFactory.createJTemplate(old.getEditingFILE()); + DesignerContext.getDesignerFrame().addAndActivateJTemplate(template); + HistoryTemplateListCache.getInstance().setCurrentEditingTemplate(template); + } + + /** + * 主代码修复后就可以删除了 + * + * 主代码任务链接 + */ + @Negative(until = "2021-09-01") + public static void reloadTemplate() { + if (isCptxJTemplate()) { + UIUtil.invokeLaterIfNeeded(() -> { + reload(); + DesignerContext.getDesignerFrame().setVisible(true); + DesignerContext.getDesignerFrame().resizeFrame(); + }); + } + } + + private static boolean isCptxJTemplate() { + JTemplate jTemplate = HistoryTemplateListCache.getInstance().getCurrentEditingTemplate(); + String path; + return jTemplate != null && + (path = jTemplate.getPath()) != null + && path.endsWith(FileExtension.CPTX.getExtension()); + } +} diff --git a/designer-realize/src/main/java/com/fr/nx/app/designer/cell/CellTreeAttrPanel.java b/designer-realize/src/main/java/com/fr/nx/app/designer/cell/CellTreeAttrPanel.java new file mode 100644 index 000000000..fcab8a861 --- /dev/null +++ b/designer-realize/src/main/java/com/fr/nx/app/designer/cell/CellTreeAttrPanel.java @@ -0,0 +1,109 @@ +package com.fr.nx.app.designer.cell; + +import com.fr.design.beans.BasicBeanPane; +import com.fr.design.event.UIObserverListener; +import com.fr.design.file.HistoryTemplateListCache; +import com.fr.design.gui.icheckbox.UICheckBox; +import com.fr.design.gui.ilable.MultilineLabel; +import com.fr.design.mainframe.CellWidgetPropertyPane; +import com.fr.design.mainframe.ElementCasePane; +import com.fr.design.mainframe.JTemplate; +import com.fr.form.ui.Widget; +import com.fr.locale.InterProviderFactory; +import com.fr.report.cell.TemplateCellElement; +import com.fr.report.web.button.form.TreeNodeToggleButton; + +import java.awt.BorderLayout; +import java.awt.Color; + +/** + * @author yaohwu + * created by yaohwu at 2020/4/26 16:33 + */ +public class CellTreeAttrPanel extends BasicBeanPane { + private static final Color TIP_COLOR = new Color(0x8f8f92); + + private TemplateCellElement cellElement = null; + + private final UICheckBox showAsTreeNodeCheckBox; + + private final UIObserverListener listener = new UIObserverListener() { + @Override + public void doChange() { + JTemplate template = HistoryTemplateListCache.getInstance().getCurrentEditingTemplate(); + if (template != null) { + if (CellTreeAttrPanel.this.cellElement != null) { + CellTreeAttrPanel.this.updateBean(); + } + template.fireTargetModified(); + CellWidgetPropertyPane.getInstance().populate((ElementCasePane) template.getCurrentElementCasePane()); + } + } + }; + + private static final class Holder { + private static final CellTreeAttrPanel INSTANCE = new CellTreeAttrPanel(); + } + + public static CellTreeAttrPanel getInstance() { + return Holder.INSTANCE; + } + + private CellTreeAttrPanel() { + this.setLayout(new BorderLayout()); + showAsTreeNodeCheckBox = + new UICheckBox(InterProviderFactory.getProvider().getLocText("Fine-Plugin_Engine_Cell_Show_As_Tree_Node")); + this.add(showAsTreeNodeCheckBox, BorderLayout.NORTH); + MultilineLabel multilineLabel = + new MultilineLabel(InterProviderFactory.getProvider().getLocText("Fine-Plugin_Engine_Cell_Show_As_Tree_Node_TIP")); + multilineLabel.setForeground(TIP_COLOR); + this.add(multilineLabel, BorderLayout.CENTER); + } + + /** + * 展示数据 + * + * @param ob 待展示的对象 + */ + @Override + public void populateBean(TemplateCellElement ob) { + this.cellElement = ob; + Widget widget = cellElement.getWidget(); + showAsTreeNodeCheckBox.registerChangeListener(null); + showAsTreeNodeCheckBox.setSelected(widget instanceof TreeNodeToggleButton); + showAsTreeNodeCheckBox.registerChangeListener(listener); + } + + /** + * 保存数据 + * + * @return 待保存的对象 + */ + @Override + public TemplateCellElement updateBean() { + if (this.cellElement == null) { + return null; + } + if (showAsTreeNodeCheckBox.isSelected()) { + cellElement.setWidget(createTreeToggleButton()); + } else { + cellElement.setWidget(null); + } + return this.cellElement; + } + + @Override + public void updateBean(TemplateCellElement ob) { + this.cellElement = ob; + updateBean(); + } + + @Override + protected String title4PopupWindow() { + return ""; + } + + private TreeNodeToggleButton createTreeToggleButton() { + return new TreeNodeToggleButton(); + } +} diff --git a/designer-realize/src/main/java/com/fr/nx/app/designer/cell/CellTreeAttrPanelProvider.java b/designer-realize/src/main/java/com/fr/nx/app/designer/cell/CellTreeAttrPanelProvider.java new file mode 100644 index 000000000..e50e9309a --- /dev/null +++ b/designer-realize/src/main/java/com/fr/nx/app/designer/cell/CellTreeAttrPanelProvider.java @@ -0,0 +1,30 @@ +package com.fr.nx.app.designer.cell; + +import com.fr.design.beans.BasicBeanPane; +import com.fr.design.file.HistoryTemplateListCache; +import com.fr.design.fun.impl.AbstractCellExpandAttrPanelProvider; +import com.fr.design.mainframe.JTemplate; +import com.fr.nx.app.designer.JStreamBook; +import com.fr.report.cell.TemplateCellElement; + +/** + * @author yaohwu + * created by yaohwu at 2020/4/26 16:32 + */ +public class CellTreeAttrPanelProvider extends AbstractCellExpandAttrPanelProvider { + + @Override + public BasicBeanPane createPanel() { + return CellTreeAttrPanel.getInstance(); + } + + /** + * 只有新引擎 cptx 模版才展示 + * 先注释掉Override,适配老的主jar,保留这个方法是为了在新主jar下支持这个功能 + */ + // @Override + public boolean isDisplayable() { + JTemplate current = HistoryTemplateListCache.getInstance().getCurrentEditingTemplate(); + return current instanceof JStreamBook; + } +} diff --git a/designer-realize/src/main/java/com/fr/nx/app/designer/menu/CalculateAttrAction.java b/designer-realize/src/main/java/com/fr/nx/app/designer/menu/CalculateAttrAction.java new file mode 100644 index 000000000..d318f725a --- /dev/null +++ b/designer-realize/src/main/java/com/fr/nx/app/designer/menu/CalculateAttrAction.java @@ -0,0 +1,76 @@ +package com.fr.nx.app.designer.menu; + +import com.fr.base.BaseUtils; +import com.fr.design.actions.JTemplateAction; +import com.fr.design.beans.BasicBeanPane; +import com.fr.design.dialog.DialogActionAdapter; +import com.fr.design.dialog.UIDialog; +import com.fr.design.mainframe.DesignerContext; +import com.fr.design.menu.MenuKeySet; +import com.fr.locale.InterProviderFactory; +import com.fr.main.impl.WorkBook; +import com.fr.nx.app.designer.JStreamBook; +import com.fr.plugin.attr.CalculatorAttrMark; + +import javax.swing.KeyStroke; +import java.awt.event.ActionEvent; + +public class CalculateAttrAction extends JTemplateAction { + + public CalculateAttrAction(JStreamBook jTemplate) { + super(jTemplate); + initMenuStyle(); + } + + private void initMenuStyle() { + this.setMenuKeySet(CALCULATE_FIT_ATTR); + this.setName(getMenuKeySet().getMenuKeySetName() + "..."); + this.setMnemonic(getMenuKeySet().getMnemonic()); + this.setSmallIcon(BaseUtils.readIcon("/com/fr/design/images/bbs/center.png")); + } + + /** + * Action触发事件 + * + * @param e 事件 + */ + public void actionPerformed(ActionEvent e) { + final JStreamBook jwb = getEditingComponent(); + if (jwb == null) { + return; + } + WorkBook workBook = jwb.getTarget(); + CalculatorAttrMark mark = workBook.getAttrMark(CalculatorAttrMark.MARK); + CalculateAttrPane attrPane = new CalculateAttrPane(); + showReportFitDialog(mark, jwb, workBook, attrPane); + } + + private void showReportFitDialog(CalculatorAttrMark mark, final JStreamBook jwb, final WorkBook workBook, final BasicBeanPane attrPane) { + attrPane.populateBean(mark); + UIDialog dialog = attrPane.showMediumWindow(DesignerContext.getDesignerFrame(), new DialogActionAdapter() { + @Override + public void doOk() { + workBook.addAttrMark(attrPane.updateBean()); + jwb.fireTargetModified(); + } + }); + dialog.setVisible(true); + } + + private static final MenuKeySet CALCULATE_FIT_ATTR = new MenuKeySet() { + @Override + public char getMnemonic() { + return 'C'; + } + + @Override + public String getMenuName() { + return InterProviderFactory.getProvider().getLocText("Fine-Plugin_Engine-Attr"); + } + + @Override + public KeyStroke getKeyStroke() { + return null; + } + }; +} \ No newline at end of file diff --git a/designer-realize/src/main/java/com/fr/nx/app/designer/menu/CalculateAttrPane.java b/designer-realize/src/main/java/com/fr/nx/app/designer/menu/CalculateAttrPane.java new file mode 100644 index 000000000..a0f53c938 --- /dev/null +++ b/designer-realize/src/main/java/com/fr/nx/app/designer/menu/CalculateAttrPane.java @@ -0,0 +1,200 @@ +package com.fr.nx.app.designer.menu; + +import com.fr.design.beans.BasicBeanPane; +import com.fr.design.dialog.DialogActionAdapter; +import com.fr.design.gui.icheckbox.UICheckBox; +import com.fr.design.gui.ilable.ActionLabel; +import com.fr.design.gui.ilable.UILabel; +import com.fr.design.gui.ispinner.UISpinner; +import com.fr.design.layout.FRGUIPaneFactory; +import com.fr.design.layout.TableLayoutHelper; +import com.fr.locale.InterProviderFactory; +import com.fr.nx.app.designer.menu.bean.FeatureFlagBean; +import com.fr.nx.feature.FeatureFlags; +import com.fr.nx.feature.FeatureManager; +import com.fr.plugin.attr.CalculatorAttrMark; + +import javax.swing.BorderFactory; +import javax.swing.BoxLayout; +import javax.swing.JDialog; +import javax.swing.JPanel; +import javax.swing.SwingUtilities; +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.Component; +import java.awt.Font; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.util.ArrayList; +import java.util.List; + +public class CalculateAttrPane extends BasicBeanPane { + + private static final Color TIPS_FONT_COLOR = new Color(0x8f8f92); + + // private UICheckBox calculateEnd; + private UICheckBox queryCache; + + private UICheckBox treeAsyncQuery; + + private UICheckBox multiSourceMode; + + private UISpinner treeExpandLayer; + + private JPanel treeConfigPanel; + + /** + * 当前模版从未设置过模版计算属性 + */ + private boolean isEmptyAttr = false; + + public CalculateAttrPane() { + initComponents(); + } + + private void initComponents() { + this.setLayout(FRGUIPaneFactory.createBorderLayout()); + this.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10)); + + JPanel featureFlagPanel = FRGUIPaneFactory.createRightFlowInnerContainer_S_Pane(); + ActionLabel featureLabel = new ActionLabel(InterProviderFactory.getProvider().getLocText("Fine-Plugin_Engine_Experimental_Feature")); + featureLabel.setFont(new Font(null, Font.PLAIN, 10)); + featureLabel.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + JDialog wDialog = createJDialog(); + wDialog.setVisible(true); + } + }); + featureFlagPanel.add(featureLabel); + + JPanel calculateAttrPanel = FRGUIPaneFactory.createY_AXISBoxInnerContainer_S_Pane(); + calculateAttrPanel.setBorder(BorderFactory.createEmptyBorder(0, 0, 4, 0)); + +// JPanel calculatedEndPanel = FRGUIPaneFactory.createNormalFlowInnerContainer_S_Pane(); +// calculateEnd = new UICheckBox(InterProviderFactory.getProvider().getLocText("Fine-Plugin_Engine-Calculate-End")); +// calculatedEndPanel.add(calculateEnd); + JPanel queryCachePanel = FRGUIPaneFactory.createNormalFlowInnerContainer_S_Pane(); + queryCache = new UICheckBox(InterProviderFactory.getProvider().getLocText("Fine-Plugin_Engine_Calculate_Query_Cache")); + queryCachePanel.add(queryCache); + JPanel multiSourcePanel = FRGUIPaneFactory.createNormalFlowInnerContainer_S_Pane(); + multiSourceMode = new UICheckBox(InterProviderFactory.getProvider().getLocText("Fine-Plugin_Engine_Feature_Multi_Source")); + multiSourcePanel.add(multiSourceMode); + +// calculateAttrPanel.add(calculatedEndPanel); + calculateAttrPanel.add(queryCachePanel); + calculateAttrPanel.add(multiSourcePanel); + + + treeConfigPanel = FRGUIPaneFactory.createTitledBorderPane( + InterProviderFactory.getProvider().getLocText("Fine-Plugin_Engine_Tree_Config") + ); + treeConfigPanel.setVisible(FeatureManager.getInstance().isFlagEnable(FeatureFlags.TREE)); + treeConfigPanel.setLayout(new BoxLayout(treeConfigPanel, BoxLayout.Y_AXIS)); + + JPanel treeAsyncQueryPanel = FRGUIPaneFactory.createNormalFlowInnerContainer_S_Pane(); + treeAsyncQuery = new UICheckBox(InterProviderFactory.getProvider().getLocText("Fine-Plugin_Engine_Tree_Async_Query")); + treeAsyncQueryPanel.add(treeAsyncQuery); + + JPanel treeExpandLayerPanel = FRGUIPaneFactory.createNormalFlowInnerContainer_S_Pane(); + UILabel treeExpandLayerLabel = new UILabel(InterProviderFactory.getProvider().getLocText("Fine-Plugin_Engine_Tree_Expand_Layer")); + treeExpandLayer = new UISpinner(1, Integer.MAX_VALUE, 1, 1); + // 功能不支持,设置先禁用 + treeExpandLayer.setEnabled(false); + + treeExpandLayerPanel.add(treeExpandLayerLabel); + treeExpandLayerPanel.add(treeExpandLayer); + + JPanel treeExpandLayerLabelPanel = FRGUIPaneFactory.createNormalFlowInnerContainer_S_Pane(); + UILabel multilineLabel = new UILabel(InterProviderFactory.getProvider().getLocText("Fine-Plugin_Engine_Tree_Expand_Layer_Label")); + multilineLabel.setForeground(TIPS_FONT_COLOR); + treeExpandLayerLabelPanel.add(multilineLabel); + + + treeConfigPanel.add(treeAsyncQueryPanel); + treeConfigPanel.add(treeExpandLayerPanel); + treeConfigPanel.add(treeExpandLayerLabelPanel); + + List components = new ArrayList<>(); + components.add(new Component[]{featureFlagPanel}); + components.add(new Component[]{calculateAttrPanel}); + components.add(new Component[]{treeConfigPanel}); + + JPanel content = + TableLayoutHelper.createGapTableLayoutPane( + components.toArray(new Component[0][]), + TableLayoutHelper.FILL_LASTCOLUMN, + 5, + 5 + ); + + this.add(content, BorderLayout.CENTER); + } + + @Override + public void populateBean(CalculatorAttrMark attrMark) { + if (attrMark == null) { + isEmptyAttr = true; + // 默认折叠树配置 + treeAsyncQuery.setSelected(FeatureManager.getInstance().isFlagEnable(FeatureFlags.TREE_ASYNC)); + return; + } +// calculateEnd.setSelected(attrMark.isCalculateEnd()); + queryCache.setSelected(attrMark.isQueryCache()); + treeAsyncQuery.setSelected(attrMark.isTreeAsyncQuery()); + treeExpandLayer.setValue(attrMark.getTreeExpandLayer()); + multiSourceMode.setSelected(attrMark.isUseMultiSourceMode()); + isEmptyAttr = false; + } + + + /** + * 提交数据 + * + * @return 界面上的更新数据 + */ + @Override + public CalculatorAttrMark updateBean() { + return new CalculatorAttrMark(false, queryCache.isSelected()) + .treeAsyncQuery(treeAsyncQuery.isSelected()) + .treeExpandLayer((int) treeExpandLayer.getValue()) + .useMultiSourceMode(multiSourceMode.isSelected()); + } + + /** + * 标题 + * + * @return 标题 + */ + @Override + protected String title4PopupWindow() { + return InterProviderFactory.getProvider().getLocText("Fine-Plugin_Engine-Attr"); + } + + private void showFeatureConfigPane() { + treeConfigPanel.setVisible(FeatureManager.getInstance().isFlagEnable(FeatureFlags.TREE)); + } + + private void resetTreeAsyncConfigPane() { + if (isEmptyAttr) { + treeAsyncQuery.setSelected(FeatureManager.getInstance().isFlagEnable(FeatureFlags.TREE_ASYNC)); + } + } + + + private JDialog createJDialog() { + final FeatureFlagsPanel panel = new FeatureFlagsPanel(); + panel.populateBean(new FeatureFlagBean()); + return panel.showSmallWindow(SwingUtilities.getWindowAncestor(CalculateAttrPane.this), new DialogActionAdapter() { + @Override + public void doOk() { + panel.updateBean().saveModification(); + super.doOk(); + CalculateAttrPane.this.resetTreeAsyncConfigPane(); + CalculateAttrPane.this.showFeatureConfigPane(); + CalculateAttrPane.this.revalidate(); + CalculateAttrPane.this.repaint(); + } + }); + } +} diff --git a/designer-realize/src/main/java/com/fr/nx/app/designer/menu/FeatureFlagsPanel.java b/designer-realize/src/main/java/com/fr/nx/app/designer/menu/FeatureFlagsPanel.java new file mode 100644 index 000000000..eaee45551 --- /dev/null +++ b/designer-realize/src/main/java/com/fr/nx/app/designer/menu/FeatureFlagsPanel.java @@ -0,0 +1,73 @@ +package com.fr.nx.app.designer.menu; + +import com.fr.design.beans.BasicBeanPane; +import com.fr.design.gui.icheckbox.UICheckBox; +import com.fr.design.layout.FRGUIPaneFactory; +import com.fr.design.layout.TableLayoutHelper; +import com.fr.locale.InterProviderFactory; +import com.fr.nx.app.designer.menu.bean.FeatureFlagBean; +import com.fr.nx.feature.FeatureFlag; +import com.fr.stable.collections.combination.Pair; + +import javax.swing.BorderFactory; +import javax.swing.JPanel; +import java.awt.BorderLayout; +import java.awt.Component; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; + +/** + * Created by loy on 2020/4/14. + */ +public class FeatureFlagsPanel extends BasicBeanPane { + + private FeatureFlagBean bean; + private JPanel mainPanel; + + public FeatureFlagsPanel() { + this.setLayout(FRGUIPaneFactory.createBorderLayout()); + this.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10)); + mainPanel = FRGUIPaneFactory.createY_AXISBoxInnerContainer_S_Pane(); + mainPanel.setBorder(BorderFactory.createEmptyBorder(0, 0, 4, 0)); + JPanel content = + TableLayoutHelper.createGapTableLayoutPane( + new Component[][]{new Component[]{mainPanel}}, + TableLayoutHelper.FILL_LASTCOLUMN, + 5, + 5 + ); + + this.add(content, BorderLayout.CENTER); + } + + @Override + public void populateBean(FeatureFlagBean featureFlagBean) { + this.bean = featureFlagBean; + mainPanel.removeAll(); + for (Pair entry : featureFlagBean.getAllFlags()) { + final FeatureFlag flag = entry.getFirst(); + Boolean selected = entry.getSecond(); + JPanel itemPanel = FRGUIPaneFactory.createNormalFlowInnerContainer_S_Pane(); + final UICheckBox cb = new UICheckBox(flag.getDisplayName()); + cb.setSelected(selected); + cb.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + bean.modifyFlag(flag, cb.isSelected()); + } + }); + itemPanel.add(cb); + mainPanel.add(itemPanel); + } + } + + @Override + public FeatureFlagBean updateBean() { + return bean; + } + + @Override + protected String title4PopupWindow() { + return InterProviderFactory.getProvider().getLocText("Fine-Plugin_Engine_Experimental_Feature"); + } +} diff --git a/designer-realize/src/main/java/com/fr/nx/app/designer/menu/bean/FeatureFlagBean.java b/designer-realize/src/main/java/com/fr/nx/app/designer/menu/bean/FeatureFlagBean.java new file mode 100644 index 000000000..4410d266d --- /dev/null +++ b/designer-realize/src/main/java/com/fr/nx/app/designer/menu/bean/FeatureFlagBean.java @@ -0,0 +1,36 @@ +package com.fr.nx.app.designer.menu.bean; + +import com.fr.nx.feature.FeatureFlag; +import com.fr.nx.feature.FeatureManager; +import com.fr.stable.collections.combination.Pair; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * Created by loy on 2020/4/14. + */ +public class FeatureFlagBean { + + private Map modifyMap = new HashMap<>(); + + public List> getAllFlags() { + List> flagList = new ArrayList<>(); + for (FeatureFlag flag : FeatureManager.getSupportFlags()) { + flagList.add(new Pair<>(flag, FeatureManager.getInstance().isFlagEnable(flag))); + } + return flagList; + } + + public void modifyFlag(FeatureFlag flag, boolean value) { + modifyMap.put(flag, value); + } + + public void saveModification() { + for (Map.Entry entry : modifyMap.entrySet()) { + FeatureManager.getInstance().setFlagEnable(entry.getKey(), entry.getValue()); + } + } +} 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 162a2044f..4bcf4387f 100644 --- a/designer-realize/src/main/java/com/fr/start/MainDesigner.java +++ b/designer-realize/src/main/java/com/fr/start/MainDesigner.java @@ -6,6 +6,7 @@ import com.fr.design.DesignerEnvManager; import com.fr.design.actions.file.WebPreviewUtils; import com.fr.design.actions.file.newReport.NewPolyReportAction; import com.fr.design.actions.file.newReport.NewWorkBookAction; +import com.fr.design.actions.server.LocalAnalyzerAction; import com.fr.design.actions.server.ServerConfigManagerAction; import com.fr.design.actions.server.StyleListAction; import com.fr.design.actions.server.WidgetManagerAction; @@ -165,6 +166,7 @@ public class MainDesigner extends BaseDesigner { if (WorkContext.getCurrent().isRoot()) { menuDef.addShortCut(new ServerConfigManagerAction(), new StyleListAction(), new WidgetManagerAction()); menuDef.addShortCut(new ChartPreStyleAction(), new ChartEmptyDataStyleAction(),new ChartMapEditorAction()); + menuDef.addShortCut(new LocalAnalyzerAction()); } insertMenu(menuDef, MenuHandler.SERVER); diff --git a/designer-realize/src/main/java/com/fr/start/module/DesignerActivator.java b/designer-realize/src/main/java/com/fr/start/module/DesignerActivator.java index 3f1aa2663..025539d79 100644 --- a/designer-realize/src/main/java/com/fr/start/module/DesignerActivator.java +++ b/designer-realize/src/main/java/com/fr/start/module/DesignerActivator.java @@ -27,6 +27,8 @@ import com.fr.design.actions.insert.flot.ImageFloatAction; import com.fr.design.actions.insert.flot.TextBoxFloatAction; import com.fr.design.bridge.DesignToolbarProvider; import com.fr.design.constants.DesignerLaunchStatus; +import com.fr.design.fit.NewJForm; +import com.fr.design.fit.common.TemplateTool; import com.fr.design.form.parameter.FormParaDesigner; import com.fr.design.fun.ElementUIProvider; import com.fr.design.gui.controlpane.NameObjectCreator; @@ -42,7 +44,7 @@ import com.fr.design.mainframe.BaseJForm; import com.fr.design.mainframe.ElementCaseThumbnail; import com.fr.design.mainframe.FormHierarchyTreePane; import com.fr.design.mainframe.InformationCollector; -import com.fr.design.mainframe.JForm; +import com.fr.design.mainframe.JTemplateEvent; import com.fr.design.mainframe.WidgetPropertyPane; import com.fr.design.mainframe.WidgetToolBarPane; import com.fr.design.mainframe.alphafine.AlphaFineHelper; @@ -62,6 +64,7 @@ import com.fr.design.parameter.WorkBookParameterReader; import com.fr.design.update.actions.RecoverForDesigner; import com.fr.design.update.push.DesignerPushUpdateManager; import com.fr.design.widget.ui.btn.FormSubmitButtonDetailPane; +import com.fr.event.EventDispatcher; import com.fr.form.stable.ElementCaseThumbnailProcessor; import com.fr.general.GeneralContext; import com.fr.general.xml.GeneralXMLTools; @@ -395,9 +398,15 @@ public class DesignerActivator extends Activator { DesignModuleFactory.registerButtonDetailPaneClass(FormSubmitButtonDetailPane.class); DesignModuleFactory.registerParameterReader(new FormParameterReader()); - StableFactory.registerMarkedClass(BaseJForm.XML_TAG, JForm.class); - + StableFactory.registerMarkedClass(BaseJForm.XML_TAG, NewJForm.class); StableFactory.registerMarkedObject(ElementCaseThumbnailProcessor.MARK_STRING, new ElementCaseThumbnail()); + + registerJTemplateEvent(); + } + + private static void registerJTemplateEvent(){ + EventDispatcher.listen(JTemplateEvent.BEFORE_TEMPLATE_INIT, TemplateTool.getSwitchListener()); + EventDispatcher.listen(JTemplateEvent.BEFORE_TEMPLATE_ACTIVE, TemplateTool.getSwitchListener()); } private static void storePassport() { diff --git a/designer-realize/src/test/java/com/fr/nx/app/designer/CptxAppTest.java b/designer-realize/src/test/java/com/fr/nx/app/designer/CptxAppTest.java new file mode 100644 index 000000000..26ca454c5 --- /dev/null +++ b/designer-realize/src/test/java/com/fr/nx/app/designer/CptxAppTest.java @@ -0,0 +1,54 @@ +package com.fr.nx.app.designer; + +import com.fr.file.AbstractFILE; +import com.fr.file.FILE; +import com.fr.main.impl.WorkBook; +import com.fr.nx.app.designer.utils.DesignerCptxFileUtils; +import org.easymock.EasyMock; +import org.junit.Assert; +import org.junit.FixMethodOrder; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; +import org.junit.runners.MethodSorters; +import org.powermock.api.easymock.PowerMock; +import org.powermock.core.classloader.annotations.PrepareForTest; +import org.powermock.modules.junit4.PowerMockRunner; +import org.powermock.modules.junit4.PowerMockRunnerDelegate; + + +@FixMethodOrder(MethodSorters.NAME_ASCENDING) +@RunWith(PowerMockRunner.class) +@PowerMockRunnerDelegate(JUnit4.class) +@PrepareForTest({ + DesignerCptxFileUtils.class +}) +public class CptxAppTest { + + @Test + public void testDefaultExtensions() { + CptxApp app = new CptxApp(); + String[] extendsions = app.defaultExtensions(); + Assert.assertEquals("cptx", extendsions[0]); + } + + @Test + public void testAsIOFile() { + PowerMock.mockStatic(DesignerCptxFileUtils.class); + FILE file = new AbstractFILE() { + }; + WorkBook workBook = new WorkBook(); + EasyMock.expect(DesignerCptxFileUtils.getWorkBook(file)) + .andReturn(workBook).once() + .andReturn(null).once(); + + PowerMock.replay(DesignerCptxFileUtils.class); + Assert.assertSame(workBook, new CptxApp().asIOFile(file)); + + Assert.assertNotNull(new CptxApp().asIOFile(file)); + + PowerMock.verify(DesignerCptxFileUtils.class); + + + } +} diff --git a/designer-realize/src/test/java/com/fr/nx/app/designer/JStreamBookTest.java b/designer-realize/src/test/java/com/fr/nx/app/designer/JStreamBookTest.java new file mode 100644 index 000000000..236096f0d --- /dev/null +++ b/designer-realize/src/test/java/com/fr/nx/app/designer/JStreamBookTest.java @@ -0,0 +1,121 @@ +package com.fr.nx.app.designer; + +import com.fr.file.FILE; +import com.fr.main.impl.WorkBook; +import com.fr.nx.app.designer.toolbar.TemplateTransformer; +import com.fr.nx.app.designer.toolbar.TransformResult; +import com.fr.nx.app.designer.toolbar.TransformResultInfo; +import org.easymock.EasyMock; +import org.easymock.IAnswer; +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.powermock.api.easymock.PowerMock; +import org.powermock.core.classloader.annotations.PowerMockIgnore; +import org.powermock.core.classloader.annotations.PrepareForTest; +import org.powermock.core.classloader.annotations.SuppressStaticInitializationFor; +import org.powermock.modules.junit4.PowerMockRunner; + +@RunWith(PowerMockRunner.class) +@PrepareForTest({TemplateTransformer.class, JStreamBook.class}) +@PowerMockIgnore({"com.fr.license.*"}) +@SuppressStaticInitializationFor({"com.fr.plugin.designer.JStreamBook"}) +public class JStreamBookTest { + + + @Test + public void saveFile() { + + PowerMock.mockStatic(TemplateTransformer.class); + final boolean[] fireSave = {false}; + + + JStreamBook workbook = + EasyMock.partialMockBuilder(VirtualJStreamBook.class) + .addMockedMethod("fireJTemplateSaved") + .addMockedMethod("getTarget") + .addMockedMethod("getEditingFILE") + .createMock(); + // 保存成功 + EasyMock.expect(TemplateTransformer.compileCPTX(null, null)) + .andReturn(TransformResultInfo.generateResult(TransformResult.UNSUPPORT, "fake Unsupported").saved(true)) + .once(); + PowerMock.replay(TemplateTransformer.class); + + workbook.fireJTemplateSaved(); + EasyMock.expectLastCall().andAnswer(new IAnswer() { + @Override + public Void answer() throws Throwable { + fireSave[0] = true; + return null; + } + }).once(); + + EasyMock.expect(workbook.getTarget()).andAnswer(new IAnswer() { + @Override + public WorkBook answer() throws Throwable { + return null; + } + }).once(); + + EasyMock.expect(workbook.getEditingFILE()).andAnswer(new IAnswer() { + @Override + public FILE answer() throws Throwable { + return null; + } + }).once(); + + EasyMock.replay(workbook); + + boolean saved = workbook.saveFile(); + Assert.assertTrue(saved); + Assert.assertTrue(fireSave[0]); + + PowerMock.verify(TemplateTransformer.class); + EasyMock.verify(workbook); + + fireSave[0] = false; + + // 保存失败 + PowerMock.reset(TemplateTransformer.class); + EasyMock.expect(TemplateTransformer.compileCPTX(null, null)) + .andReturn(TransformResultInfo.generateResult(TransformResult.FAILED, "fake Unsupported").saved(false)) + .once(); + PowerMock.replay(TemplateTransformer.class); + EasyMock.reset(workbook); + + EasyMock.expect(workbook.getTarget()).andAnswer(new IAnswer() { + @Override + public WorkBook answer() throws Throwable { + return null; + } + }).once(); + + EasyMock.expect(workbook.getEditingFILE()).andAnswer(new IAnswer() { + @Override + public FILE answer() throws Throwable { + return null; + } + }).once(); + EasyMock.replay(workbook); + + saved = workbook.saveFile(); + Assert.assertFalse(saved); + Assert.assertFalse(fireSave[0]); + + PowerMock.verify(TemplateTransformer.class); + EasyMock.verify(workbook); + } + + private static final class VirtualJStreamBook extends JStreamBook { + + public VirtualJStreamBook(WorkBook workBook, FILE file) { + super(workBook, file); + } + + @Override + public String toString() { + return this.getClass().getName() + "fake to string"; + } + } +} \ No newline at end of file diff --git a/designer-realize/src/test/resources/cpt/read-write-expand-order.cpt b/designer-realize/src/test/resources/cpt/read-write-expand-order.cpt new file mode 100644 index 000000000..3401f6fcc --- /dev/null +++ b/designer-realize/src/test/resources/cpt/read-write-expand-order.cpt @@ -0,0 +1,79 @@ + + + + +
+ + + +
+ + + + + + + + + + + + + + + + + + + + + +tC;K&-B[7`5WK?,Xelf@&8+0+s;]Ad+gFe2#R^R0KRcH9&1(_,Q&m?CF2#j +L2g`#\[sRl3^"LL;I*q<:m`bRtAnUh^m(8[R[\Qdap[PoDZI+D(a6:V.`2I2?>R^Sd'MY!@@k@GfZiTaUt[[X*Qn(Vqi1,QX!S[g +X8cV#:o/U(RN!]A[-MCK!$>RYIs_SOW[_9PE1L2Q+u4)L'*_+9GA>FbhqkF3>rH*rsS)m6q"c>/>qr/Vjd(?R(*:-.oLG%<9eZ1]Al^J"e-:(+>+Q1NPOaSJo+/S+i4n&T% +\/J4IBcE%5,_GkC[%jH'kh$2]A+l)FI""N?9]AXFHef&/Q\Pm:q*oGRR,WLM]A._;-d'aOX+8Xcia>YISk1g?.bi%E?!"e!Dq4AIRE5*J06XZ"l9/,[%5lSYHM*Gsc;De0X(/o,#On+F;om!UF7s$[mR(>_*`AIpT;n1NSp*r +5Md*5kOL1RpseeaT5!G,kk_1&F%\@jJs#W%4nbD$X(dC(\O+&KX.Vp>]AY-LoL^V=LM7!$S/!&hR'nH_0hm_H,f=2p+p`R&".Le>"lHKmKAHnSIm69U'g +LYG,>[,VlJ&!5>PutHqRJ4XG^$7ZL6(YG+-e=+b;LUuH3Zrc58GNFlF?j1-+QXQ(%S6.V?:G +1jf/GeC6q@_Q>S5"##8tiN'(`qU.T7U?<]A'i8Uhe/g^>U%!HX@Al,.N83)TcC)q$'k_OQ-?: +_D-F="?_k3O8O[DbknE3?fn3U+EJ3:q;#XccA+E^R:k_hA7K7g.e*(3U#a[& +%V(jo6,q3)+CL/"I32M"cKPBGLn2M%"r'=faV2lDBHCm%MHBI]AOIZj%Z*1+*FVK8J!g'*3c# +"WMR3*Y&1h_H@PEJC*H;"Y@nbp:f4UB61F[-H4phL"^(VI"gQST8h*)bt+Jo(MXVpo+u.k@X +tpaqkFs9$ + + + + + + + + + + + + + +
+ + + + + + + + + + + +
diff --git a/designer-realize/src/test/resources/cpt/read-write-image-ref.cpt b/designer-realize/src/test/resources/cpt/read-write-image-ref.cpt new file mode 100644 index 000000000..62797764a --- /dev/null +++ b/designer-realize/src/test/resources/cpt/read-write-image-ref.cpt @@ -0,0 +1,78 @@ + + + + +
+ + + +
+ + + + + + + + + + + + + + + + + + + + +tC;K&-B[7`5WK?,Xelf@&8+0+s;]Ad+gFe2#R^R0KRcH9&1(_,Q&m?CF2#j +L2g`#\[sRl3^"LL;I*q<:m`bRtAnUh^m(8[R[\Qdap[PoDZI+D(a6:V.`2I2?>R^Sd'MY!@@k@GfZiTaUt[[X*Qn(Vqi1,QX!S[g +X8cV#:o/U(RN!]A[-MCK!$>RYIs_SOW[_9PE1L2Q+u4)L'*_+9GA>FbhqkF3>rH*rsS)m6q"c>/>qr/Vjd(?R(*:-.oLG%<9eZ1]Al^J"e-:(+>+Q1NPOaSJo+/S+i4n&T% +\/J4IBcE%5,_GkC[%jH'kh$2]A+l)FI""N?9]AXFHef&/Q\Pm:q*oGRR,WLM]A._;-d'aOX+8Xcia>YISk1g?.bi%E?!"e!Dq4AIRE5*J06XZ"l9/,[%5lSYHM*Gsc;De0X(/o,#On+F;om!UF7s$[mR(>_*`AIpT;n1NSp*r +5Md*5kOL1RpseeaT5!G,kk_1&F%\@jJs#W%4nbD$X(dC(\O+&KX.Vp>]AY-LoL^V=LM7!$S/!&hR'nH_0hm_H,f=2p+p`R&".Le>"lHKmKAHnSIm69U'g +LYG,>[,VlJ&!5>PutHqRJ4XG^$7ZL6(YG+-e=+b;LUuH3Zrc58GNFlF?j1-+QXQ(%S6.V?:G +1jf/GeC6q@_Q>S5"##8tiN'(`qU.T7U?<]A'i8Uhe/g^>U%!HX@Al,.N83)TcC)q$'k_OQ-?: +_D-F="?_k3O8O[DbknE3?fn3U+EJ3:q;#XccA+E^R:k_hA7K7g.e*(3U#a[& +%V(jo6,q3)+CL/"I32M"cKPBGLn2M%"r'=faV2lDBHCm%MHBI]AOIZj%Z*1+*FVK8J!g'*3c# +"WMR3*Y&1h_H@PEJC*H;"Y@nbp:f4UB61F[-H4phL"^(VI"gQST8h*)bt+Jo(MXVpo+u.k@X +tpaqkFs9$ + + + + + + + + + + + + + +
+ + + + + + + + + + + +