Browse Source

Merge remote-tracking branch 'origin/fbp/release' into fbp/release

fbp/release
Anner 1 month ago
parent
commit
f3195060a4
  1. 14
      designer-base/src/main/java/com/fine/theme/light/ui/FineTableHeaderUI.java
  2. 3
      designer-base/src/main/java/com/fr/design/ExtraDesignClassManager.java
  3. 4
      designer-base/src/main/java/com/fr/design/actions/file/PreferencePane.java
  4. 7
      designer-base/src/main/java/com/fr/design/data/BasicTableDataTreePane.java
  5. 33
      designer-base/src/main/java/com/fr/design/data/datapane/ChoosePane.java
  6. 9
      designer-base/src/main/java/com/fr/design/data/datapane/TableDataComboBox.java
  7. 7
      designer-base/src/main/java/com/fr/design/data/datapane/TableDataTreePane.java
  8. 15
      designer-base/src/main/java/com/fr/design/data/datapane/auth/TableDataAuthHelper.java
  9. 11
      designer-base/src/main/java/com/fr/design/data/datapane/connect/ConnectionComboBoxPanel.java
  10. 5
      designer-base/src/main/java/com/fr/design/data/datapane/preview/ConnectionInfoBeanHelper.java
  11. 30
      designer-base/src/main/java/com/fr/design/debug/edt/StrictEDTException.java
  12. 130
      designer-base/src/main/java/com/fr/design/debug/edt/StrictEdtListeners.java
  13. 135
      designer-base/src/main/java/com/fr/design/debug/edt/StrictEdtManager.java
  14. 15
      designer-base/src/main/java/com/fr/design/debug/ui/UIMonitorPane.java
  15. 85
      designer-base/src/main/java/com/fr/design/editor/editor/ColumnRowGroupEditor.java
  16. 23
      designer-base/src/main/java/com/fr/design/file/MultiTemplateTabPane.java
  17. 21
      designer-base/src/main/java/com/fr/design/gui/itree/refreshabletree/RefreshableJTree.java
  18. 56
      designer-base/src/main/java/com/fr/design/i18n/DesignExtendLanguageConfig.java
  19. 3
      designer-base/src/main/java/com/fr/design/mainframe/CenterRegionContainerPane.java
  20. 6
      designer-base/src/main/java/com/fr/design/mainframe/JTemplate.java
  21. 19
      designer-base/src/main/java/com/fr/design/utils/DesignUtils.java
  22. 22
      designer-base/src/main/java/com/fr/design/write/submit/DBManipulationPane.java
  23. 23
      designer-base/src/main/java/com/fr/file/FILEChooserPane.java
  24. 13
      designer-base/src/main/resources/com/fine/theme/icon/editor/cell_group_popup.svg
  25. 13
      designer-base/src/main/resources/com/fine/theme/icon/editor/cell_group_popup_disable.svg
  26. 1
      designer-base/src/main/resources/com/fine/theme/light/ui/fine_light.icon.json
  27. 120
      designer-base/src/main/resources/com/fr/design/data/tabledata/datacenter/web/data-choose.main.js
  28. 2
      designer-base/src/main/resources/com/fr/design/i18n/dimension_en.properties
  29. 2
      designer-realize/src/main/java/com/fanruan/boot/env/function/app/DesignAppComponent.java
  30. 4
      designer-realize/src/main/java/com/fr/start/MainDesigner.java

14
designer-base/src/main/java/com/fine/theme/light/ui/FineTableHeaderUI.java

@ -124,14 +124,24 @@ public class FineTableHeaderUI extends FlatTableHeaderUI {
* @param column
*/
private void setToolTipText(JTable table, Object value, int column) {
String tipText = value == null ? StringUtils.BLANK : String.valueOf(value);
String tipText = value == null ? StringUtils.BLANK : getTooltipValue(value);
FontMetrics fm = this.getFontMetrics(this.getFont());
int textWidth = fm.stringWidth(String.valueOf(value));
int textWidth = fm.stringWidth(tipText);
if (textWidth > table.getColumnModel().getColumn(column).getWidth()) {
setToolTipText(tipText);
} else {
setToolTipText(null);
}
}
/**
* 提取实际显示值
*
* @param value
* @return 转换后的显示值
*/
public String getTooltipValue(Object value) {
return String.valueOf(value);
}
}
}

3
designer-base/src/main/java/com/fr/design/ExtraDesignClassManager.java

@ -4,6 +4,7 @@
package com.fr.design;
import com.fine.theme.icon.LazyIcon;
import com.fr.base.BaseUtils;
import com.fr.common.annotations.Open;
import com.fr.design.data.datapane.TableDataNameObjectCreator;
@ -143,7 +144,7 @@ public class ExtraDesignClassManager extends AbstractExtraClassManager implement
WidgetOption createT(ToolbarItemProvider provider) {
return WidgetOptionFactory.createByWidgetClass(
provider.nameForWidget(),
IOUtils.readIcon(provider.iconPathForWidget()),
new LazyIcon(provider.iconPathForWidget()),
provider.classForWidget()
);
}

4
designer-base/src/main/java/com/fr/design/actions/file/PreferencePane.java

@ -43,6 +43,7 @@ import com.fr.design.mainframe.vcs.ui.UIPositiveIntEditor;
import com.fr.design.mainframe.vcs.ui.VcsMovePanel;
import com.fr.design.os.impl.SupportOSImpl;
import com.fr.design.unit.UnitConvertUtil;
import com.fr.design.utils.DesignUtils;
import com.fr.design.utils.gui.GUICoreUtils;
import com.fr.design.widget.FRWidgetFactory;
import com.fr.general.ComparatorUtils;
@ -50,7 +51,6 @@ import com.fr.general.FRFont;
import com.fr.general.Inter;
import com.fr.general.log.Log4jConfig;
import com.fr.io.attr.ImageExportAttr;
import com.fr.locale.InterProviderFactory;
import com.fr.log.FineLoggerFactory;
import com.fr.report.ReportConfigManager;
import com.fr.stable.Constants;
@ -774,7 +774,7 @@ public class PreferencePane extends BasicPane {
}
private UIDictionaryComboBox<Locale> createLanguageComboBox() {
Map<Locale, String> map = InterProviderFactory.getProvider().getSupportLocaleMap();
Map<Locale, String> map = DesignUtils.getAvailableLanguages();
int size = map.size();
Locale[] keys = new Locale[size];
String[] values = new String[size];

7
designer-base/src/main/java/com/fr/design/data/BasicTableDataTreePane.java

@ -38,6 +38,7 @@ import com.fr.general.NameObject;
import com.fr.log.FineLoggerFactory;
import com.fr.stable.StringUtils;
import com.fr.workspace.WorkContext;
import com.fr.workspace.server.repository.connection.ConnectionRepository;
import javax.swing.AbstractButton;
import javax.swing.Action;
@ -471,11 +472,11 @@ public abstract class BasicTableDataTreePane extends DockingView implements Resp
new SwingWorker<Boolean, Void>() {
@Override
protected Boolean doInBackground() throws Exception {
// 获取无权限连接名称集合
Collection<String> noAuthConnections = TableDataAuthHelper.getNoAuthConnections();
// 获取有权限的连接名称集合
Collection<String> authConnections = ConnectionRepository.getInstance().getConnNames();
// 获取当前数据集对应的数据连接名称
String connectionName = TableDataAuthHelper.getConnectionNameByDBTableData((DBTableData) wrapper.getTableData());
return !noAuthConnections.contains(connectionName);
return authConnections.contains(connectionName);
}
@Override

33
designer-base/src/main/java/com/fr/design/data/datapane/ChoosePane.java

@ -1,9 +1,7 @@
package com.fr.design.data.datapane;
import com.fanruan.config.impl.data.ConnectionConfigProviderFactory;
import com.fr.base.TableData;
import com.fr.data.core.DataCoreUtils;
import com.fr.data.core.db.DBUtils;
import com.fr.data.core.db.TableProcedure;
import com.fr.data.core.db.dialect.Dialect;
import com.fr.data.core.db.dialect.DialectFactory;
@ -39,14 +37,9 @@ import com.fr.general.IOUtils;
import com.fr.log.FineLoggerFactory;
import com.fr.stable.StringUtils;
import com.fr.workspace.WorkContext;
import com.fr.workspace.server.repository.authority.RemoteAuthorityRepository;
import com.fr.workspace.server.repository.connection.ConnectionRepository;
import javax.swing.JList;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JTree;
import javax.swing.SwingWorker;
import javax.swing.*;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.event.PopupMenuEvent;
@ -56,21 +49,14 @@ import javax.swing.tree.DefaultTreeCellRenderer;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.TreeCellRenderer;
import javax.swing.tree.TreePath;
import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.*;
import java.awt.event.FocusAdapter;
import java.awt.event.FocusEvent;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.CancellationException;
/**
@ -339,20 +325,7 @@ public class ChoosePane extends BasicBeanPane<DataBaseItems> implements Refresha
}
private List<String> getHasAuthConnections() {
List<String> authConnections = new ArrayList<>();
Set<String> allConnections = new HashSet<>(ConnectionRepository.getInstance().getConnNames());
// 待实现
Collection<String> noAuthConnections = RemoteAuthorityRepository.getInstance().getNoAuthConnections();
if (noAuthConnections == null) {
return authConnections;
}
for (String name : allConnections) {
if (!noAuthConnections.contains(name)) {
authConnections.add(name);
}
}
return authConnections;
return ConnectionRepository.getInstance().getConnNames();
}
/**

9
designer-base/src/main/java/com/fr/design/data/datapane/TableDataComboBox.java

@ -1,6 +1,7 @@
package com.fr.design.data.datapane;
import com.fr.design.constants.UIConstants;
import com.fr.design.ui.util.UIUtil;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.event.ItemEvent;
@ -73,7 +74,7 @@ public class TableDataComboBox extends UIComboBox implements Prepare4DataSourceC
public TableDataComboBox() {
super();
setListCellRenderer();
addListener();
addComboBoxListener();
}
/**
@ -109,14 +110,14 @@ public class TableDataComboBox extends UIComboBox implements Prepare4DataSourceC
}
/**
* 在comboBox可见时添加下拉事件与数据集响应事件
* 在comboBox可见时添加数据集响应事件与refresh操作
*/
private void addListener() {
private void addComboBoxListener() {
this.addAncestorListener(new AncestorListener() {
@Override
public void ancestorAdded(AncestorEvent event) {
registerDSChangeListener();
refresh(DesignTableDataManager.getEditingTableDataSource());
UIUtil.invokeLaterIfNeeded(() -> refresh(DesignTableDataManager.getEditingTableDataSource()));
}
@Override

7
designer-base/src/main/java/com/fr/design/data/datapane/TableDataTreePane.java

@ -71,6 +71,7 @@ import com.fr.plugin.observer.PluginEventListener;
import com.fr.stable.StringUtils;
import com.fr.stable.core.PropertyChangeAdapter;
import com.fr.workspace.WorkContext;
import com.fr.workspace.server.repository.connection.ConnectionRepository;
import org.jetbrains.annotations.NotNull;
import javax.swing.Icon;
@ -1056,11 +1057,11 @@ public class TableDataTreePane extends BasicTableDataTreePane {
new SwingWorker<Boolean, Void>() {
@Override
protected Boolean doInBackground() throws Exception {
// 获取无权限连接名称集合
Collection<String> noAuthConnections = TableDataAuthHelper.getNoAuthConnections();
// 获取有权限的连接名称集合
Collection<String> authConnections = ConnectionRepository.getInstance().getConnNames();
// 获取当前数据集对应的数据连接名称
String connectionName = TableDataAuthHelper.getConnectionNameByDBTableData((DBTableData) wrapper.getTableData());
return !noAuthConnections.contains(connectionName);
return authConnections.contains(connectionName);
}
@Override

15
designer-base/src/main/java/com/fr/design/data/datapane/auth/TableDataAuthHelper.java

@ -6,11 +6,6 @@ import com.fr.data.impl.DBTableData;
import com.fr.data.impl.NameDatabaseConnection;
import com.fr.stable.StringUtils;
import com.fr.workspace.WorkContext;
import com.fr.workspace.server.connection.DBConnectAuth;
import com.fr.workspace.server.repository.authority.RemoteAuthorityRepository;
import java.util.Collection;
import java.util.Collections;
/**
* 数据连接权限相关的工具类
@ -28,16 +23,6 @@ public class TableDataAuthHelper {
return !WorkContext.getCurrent().isLocal() && tableData instanceof DBTableData;
}
/**
* 获取无权限数据连接集合
* 远程下需要调用RPC为耗时操作谨慎使用
* @return
*/
public static Collection<String> getNoAuthConnections() {
// 获取无权限连接集合
Collection<String> noAuthConnections = RemoteAuthorityRepository.getInstance().getNoAuthConnections();
return noAuthConnections == null ? Collections.emptyList() : noAuthConnections;
}
/**
* 通过数据集获取其数据连接的名称

11
designer-base/src/main/java/com/fr/design/data/datapane/connect/ConnectionComboBoxPanel.java

@ -13,7 +13,6 @@ import com.fr.design.gui.ibutton.UILockButton;
import com.fr.report.LockItem;
import com.fr.stable.StringUtils;
import com.fr.workspace.WorkContext;
import com.fr.workspace.server.repository.authority.RemoteAuthorityRepository;
import javax.swing.SwingUtilities;
import java.awt.Dimension;
@ -22,7 +21,6 @@ import java.awt.event.ActionListener;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
@ -80,19 +78,10 @@ public class ConnectionComboBoxPanel extends ItemEditableComboBoxPanel {
* 刷新ComboBox.items
*/
protected Iterator<String> items() {
Collection<String> noAuthConnections = RemoteAuthorityRepository.getInstance().getNoAuthConnections();
nameList = new ArrayList<>();
if (noAuthConnections == null) {
return nameList.iterator();
}
final Map<String, Connection> connectionMap = ConnectionInfoBeanHelper.getCurrentConnectionMap();
for (Map.Entry<String, Connection> connectionEntry : connectionMap.entrySet()) {
String conName = connectionEntry.getKey();
if (noAuthConnections.contains(conName)) {
continue;
}
filterConnection(connectionEntry.getValue(), conName, nameList);
}
return nameList.iterator();

5
designer-base/src/main/java/com/fr/design/data/datapane/preview/ConnectionInfoBeanHelper.java

@ -40,6 +40,7 @@ import com.fr.workspace.server.repository.WorkplaceConstants;
import com.fr.workspace.server.repository.connection.ConnectionCacheEvent;
import com.fr.workspace.server.repository.connection.ConnectionRepository;
import com.fr.workspace.server.repository.tabledata.DataEncryptionHelper;
import org.jetbrains.annotations.Nullable;
import java.util.HashMap;
import java.util.Map;
@ -123,8 +124,12 @@ public class ConnectionInfoBeanHelper {
/**
* 根据bean创建连接
*/
@Nullable
public static Connection createConnection(ConnectionInfoBean infoBean) {
try {
if (infoBean == null) {
return null;
}
Connection connection = cache.get(VALUE).get(infoBean.getConnectionName());
if (connection != null) {
connection.setConnectionName(infoBean.getConnectionName());

30
designer-base/src/main/java/com/fr/design/debug/edt/StrictEDTException.java

@ -0,0 +1,30 @@
package com.fr.design.debug.edt;
/**
* Swing组件严格限制EDT运行
*
* @author vito
* @since 11.0
* Created on 2023/8/9
*/
public class StrictEDTException extends RuntimeException {
public StrictEDTException() {
}
public StrictEDTException(String message) {
super(message);
}
public StrictEDTException(String message, Throwable cause) {
super(message, cause);
}
public StrictEDTException(Throwable cause) {
super(cause);
}
public StrictEDTException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
super(message, cause, enableSuppression, writableStackTrace);
}
}

130
designer-base/src/main/java/com/fr/design/debug/edt/StrictEdtListeners.java

@ -0,0 +1,130 @@
package com.fr.design.debug.edt;
import com.fr.design.ui.util.EdtInvocationManager;
import com.fr.log.FineLoggerFactory;
import java.awt.event.ComponentEvent;
import java.awt.event.ComponentListener;
import java.awt.event.HierarchyEvent;
import java.awt.event.HierarchyListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.event.MouseWheelEvent;
import java.awt.event.MouseWheelListener;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
/**
* Swing组件严格限制EDT运行监听器
*
* @author vito
* @since 11.0
* Created on 2023/8/9
*/
public class StrictEdtListeners implements HierarchyListener, PropertyChangeListener, ComponentListener, MouseListener,
MouseWheelListener, MouseMotionListener, KeyListener {
@Override
public void componentResized(ComponentEvent e) {
checkEventDispatchThread();
}
@Override
public void componentMoved(ComponentEvent e) {
checkEventDispatchThread();
}
@Override
public void componentShown(ComponentEvent e) {
checkEventDispatchThread();
}
@Override
public void componentHidden(ComponentEvent e) {
checkEventDispatchThread();
}
@Override
public void hierarchyChanged(HierarchyEvent e) {
checkEventDispatchThread();
}
@Override
public void keyTyped(KeyEvent e) {
checkEventDispatchThread();
}
@Override
public void keyPressed(KeyEvent e) {
checkEventDispatchThread();
}
@Override
public void keyReleased(KeyEvent e) {
checkEventDispatchThread();
}
@Override
public void mouseClicked(MouseEvent e) {
checkEventDispatchThread();
}
@Override
public void mousePressed(MouseEvent e) {
checkEventDispatchThread();
}
@Override
public void mouseReleased(MouseEvent e) {
checkEventDispatchThread();
}
@Override
public void mouseEntered(MouseEvent e) {
checkEventDispatchThread();
}
@Override
public void mouseExited(MouseEvent e) {
checkEventDispatchThread();
// redispatchMouseEvent(e);
}
@Override
public void mouseDragged(MouseEvent e) {
checkEventDispatchThread();
}
@Override
public void mouseMoved(MouseEvent e) {
checkEventDispatchThread();
}
@Override
public void mouseWheelMoved(MouseWheelEvent e) {
checkEventDispatchThread();
}
@Override
public void propertyChange(PropertyChangeEvent e) {
checkEventDispatchThread();
}
/**
* 检查当前是否处于EDT中并发出告警
*/
public static void checkEventDispatchThread() {
if (!EdtInvocationManager.getInstance().isEventDispatchThread()) {
String s = String.format(
"[StrictEDT] The current operation can only be in an EDT (Event Dispatch Thread). Current thread is: %s",
Thread.currentThread().getName()
);
StrictEDTException strictEdtException = new StrictEDTException(s);
FineLoggerFactory.getLogger().warn(s, strictEdtException);
}
}
}

135
designer-base/src/main/java/com/fr/design/debug/edt/StrictEdtManager.java

@ -0,0 +1,135 @@
package com.fr.design.debug.edt;
import com.fanruan.gui.InspectorWindow;
import com.fr.design.mainframe.DesignerContext;
import com.fr.log.FineLoggerFactory;
import org.jetbrains.annotations.NotNull;
import java.awt.AWTEvent;
import java.awt.Component;
import java.awt.Container;
import java.awt.Toolkit;
import java.awt.Window;
import java.awt.event.AWTEventListener;
import java.awt.event.ContainerEvent;
/**
* 严格UI线程运行管理器
*
* @author vito
* @since 11.0
* Created on 2024/12/20
*/
public class StrictEdtManager {
private static final StrictEdtListeners LISTENERS = new StrictEdtListeners();
private static void installContainerEDTCheckers(@NotNull final Container component, int level) {
if (FineLoggerFactory.getLogger().isDebugEnabled()) {
for (int i = 0; i < level; i++) {
FineLoggerFactory.getLogger().debug(" ");
}
FineLoggerFactory.getLogger().debug(component.toString());
}
int count = component.getComponentCount();
level += 1;
for (int i = 0; i < count; i++) {
Component comp = component.getComponent(i);
addEDTCheckersListener(comp);
if (comp instanceof Container) {
installContainerEDTCheckers((Container) comp, level);
}
}
}
private static void addEDTCheckersListener(Component comp) {
comp.addHierarchyListener(LISTENERS);
comp.addPropertyChangeListener(LISTENERS);
comp.addComponentListener(LISTENERS);
comp.addMouseListener(LISTENERS);
comp.addMouseMotionListener(LISTENERS);
comp.addKeyListener(LISTENERS);
}
private static void installEDTCheckers(Container container, int level) {
installContainerEDTCheckers(container, level);
level += 1;
if (container instanceof Window) {
Window[] children = ((Window) container).getOwnedWindows();
for (Window child : children) {
if (child instanceof InspectorWindow) {
continue;
}
installEDTCheckers(child, level);
}
}
}
private static final AWTEventListener AWT_EVENT_LISTENER = (AWTEvent event) -> {
if (event instanceof ContainerEvent) {
Component child = event.getID() == ContainerEvent.COMPONENT_ADDED ? ((ContainerEvent) event).getChild() : null;
if (child != null) {
addEDTCheckersListener(child);
}
}
};
/**
* 监听组件警告不在EDT中执行的UI操作
*/
public static void install() {
// 监听当前的组件
installEDTCheckers(DesignerContext.getDesignerFrame(), 0);
// 监听新增的组件
Toolkit.getDefaultToolkit().addAWTEventListener(AWT_EVENT_LISTENER, AWTEvent.CONTAINER_EVENT_MASK);
FineLoggerFactory.getLogger().info("[StrictEDT] install Strict EDT Checkers");
}
private static void uninstallContainerEDTCheckers(@NotNull final Container component, int level) {
int count = component.getComponentCount();
level += 1;
for (int i = 0; i < count; i++) {
Component comp = component.getComponent(i);
removeEDTCheckersListener(comp);
if (comp instanceof Container) {
uninstallContainerEDTCheckers((Container) comp, level);
}
}
}
private static void removeEDTCheckersListener(Component comp) {
comp.removeHierarchyListener(LISTENERS);
comp.removePropertyChangeListener(LISTENERS);
comp.removeComponentListener(LISTENERS);
comp.removeMouseListener(LISTENERS);
comp.removeMouseMotionListener(LISTENERS);
comp.removeKeyListener(LISTENERS);
}
private static void removeEDTCheckers(Container container, int level) {
uninstallContainerEDTCheckers(container, level);
level += 1;
if (container instanceof Window) {
Window[] children = ((Window) container).getOwnedWindows();
for (Window child : children) {
if (child instanceof InspectorWindow) {
continue;
}
removeEDTCheckers(child, level);
}
}
}
/**
* 监听组件警告不在EDT中执行的UI操作
*/
public static void uninstall() {
// 取消监听新增的组件
Toolkit.getDefaultToolkit().removeAWTEventListener(AWT_EVENT_LISTENER);
// 解除监听当前的组件
removeEDTCheckers(DesignerContext.getDesignerFrame(), 0);
FineLoggerFactory.getLogger().info("[StrictEDT] uninstall Strict EDT Checkers");
}
}

15
designer-base/src/main/java/com/fr/design/debug/ui/UIMonitorPane.java

@ -8,6 +8,7 @@ import com.formdev.flatlaf.util.ScaledEmptyBorder;
import com.fr.base.extension.FileExtension;
import com.fr.design.border.FineBorderFactory;
import com.fr.design.carton.latency.LatencyLevel;
import com.fr.design.debug.edt.StrictEdtManager;
import com.fr.design.dialog.BasicDialog;
import com.fr.design.dialog.BasicPane;
import com.fr.design.gui.ibutton.UIButton;
@ -61,6 +62,7 @@ public class UIMonitorPane extends JPanel {
private DefaultTableModel model;
private UICheckBox inspector;
private UICheckBox strictEDTChecker;
private UICheckBox monitor;
public UIMonitorPane() {
@ -73,6 +75,7 @@ public class UIMonitorPane extends JPanel {
UITableScrollPane tablePane = initLatencyTable();
Row topSettingRow = initTopSettingRow();
inspector = new UICheckBox("Open UI Inspector");
strictEDTChecker = new UICheckBox("Open Strict EDT Checker");
monitor = new UICheckBox("Open Latency Monitor");
JPanel monitorPane = column(10,
@ -81,6 +84,7 @@ public class UIMonitorPane extends JPanel {
add(column(10,
cell(FineUIUtils.wrapComponentWithTitle(inspector, "UI Inspector")),
cell(FineUIUtils.wrapComponentWithTitle(strictEDTChecker, "Strict EDT Checker")),
cell(FineUIUtils.wrapComponentWithTitle(monitorPane, "UI Latency Monitor"))
).getComponent(), BorderLayout.CENTER);
@ -94,14 +98,21 @@ public class UIMonitorPane extends JPanel {
inspector.setSelected(UIInspectorHolder.getInstance().isInstalled());
monitor.setSelected(UILatencyWorker.getInstance().isMonitoring());
// 注册事件监听
inspector.addChangeListener(e -> {
inspector.addActionListener(e -> {
if (inspector.isSelected()) {
UIInspectorHolder.getInstance().install();
} else {
UIInspectorHolder.getInstance().uninstall();
}
});
monitor.addChangeListener(e -> {
strictEDTChecker.addActionListener(e -> {
if (strictEDTChecker.isSelected()) {
StrictEdtManager.install();
} else {
StrictEdtManager.uninstall();
}
});
monitor.addActionListener(e -> {
topSettingRow.setVisible(monitor.isSelected());
tablePane.setVisible(monitor.isSelected());
if (monitor.isSelected()) {

85
designer-base/src/main/java/com/fr/design/editor/editor/ColumnRowGroupEditor.java

@ -1 +1,84 @@
package com.fr.design.editor.editor; import com.fr.design.gui.itextfield.UITextField; import com.fr.design.layout.FRGUIPaneFactory; import com.fr.stable.ColumnRowGroup; import java.awt.*; /** * Author : Shockway * Date: 14-1-10 * Time: 下午1:46 */ public class ColumnRowGroupEditor extends Editor<ColumnRowGroup> { private UITextField crEditor; public ColumnRowGroupEditor() { this(""); } public ColumnRowGroupEditor(String name) { this(null, name); } public ColumnRowGroupEditor(ColumnRowGroup value) { this(value, ""); } public ColumnRowGroupEditor(ColumnRowGroup value, String name) { this.setLayout(FRGUIPaneFactory.createBorderLayout()); crEditor = new UITextField(); this.add(crEditor, BorderLayout.CENTER); this.setValue(value); this.setName(name); } @Override public ColumnRowGroup getValue() { return new ColumnRowGroup(this.crEditor.getText()); } @Override public void setValue(ColumnRowGroup value) { if (value == null) { this.crEditor.setText(""); } else { this.crEditor.setText(value.toString()); } } @Override public void setEnabled(boolean enabled) { super.setEnabled(enabled); this.crEditor.setEnabled(enabled); } /** * 获取焦点 */ public void requestFocus() { this.crEditor.requestFocus(); } public String getIconName() { return "cell_group"; } /** * 是否接收/支持这个对象 * @param object 检测对象 * @return 是否支持 */ public boolean accept(Object object) { return object instanceof ColumnRowGroup; } }
package com.fr.design.editor.editor;
import com.fr.design.gui.itextfield.UITextField;
import com.fr.design.layout.FRGUIPaneFactory;
import com.fr.stable.ColumnRowGroup;
import java.awt.*;
/**
* 单元格组编辑
*
* @author Shockway
* @since 2014-01-10
* Created on 2024-01-10
*/
public class ColumnRowGroupEditor extends Editor<ColumnRowGroup> {
private UITextField crEditor;
public ColumnRowGroupEditor() {
this("");
}
public ColumnRowGroupEditor(String name) {
this(null, name);
}
public ColumnRowGroupEditor(ColumnRowGroup value) {
this(value, "");
}
public ColumnRowGroupEditor(ColumnRowGroup value, String name) {
this.setLayout(FRGUIPaneFactory.createBorderLayout());
crEditor = new UITextField();
this.add(crEditor, BorderLayout.CENTER);
this.setValue(value);
this.setName(name);
}
@Override
public ColumnRowGroup getValue() {
return new ColumnRowGroup(this.crEditor.getText());
}
@Override
public void setValue(ColumnRowGroup value) {
if (value == null) {
this.crEditor.setText("");
} else {
this.crEditor.setText(value.toString());
}
}
@Override
public void setEnabled(boolean enabled) {
super.setEnabled(enabled);
this.crEditor.setEnabled(enabled);
}
/**
* 获取焦点
*/
public void requestFocus() {
this.crEditor.requestFocus();
}
public String getIconName() {
return "cell_group";
}
public String getIconId() {
return "cell_group_popup";
}
/**
* 是否接收/支持这个对象
* @param object 检测对象
* @return 是否支持
*/
public boolean accept(Object object) {
return object instanceof ColumnRowGroup;
}
}

23
designer-base/src/main/java/com/fr/design/file/MultiTemplateTabPane.java

@ -116,7 +116,6 @@ public class MultiTemplateTabPane extends Row {
private boolean hoverMoreAction = false;
private Icon clodeIcon = new LazyIcon("clear");
private boolean isShowList = false;
private JButton leadingActionButton;
private final UIToolbar leadingToolBar = new UIToolbar();
//自动新建的模板B若没有进行任何编辑,切换到其他
@ -203,17 +202,6 @@ public class MultiTemplateTabPane extends Row {
updateLeadingToolBar();
}
/**
* 为前导动作添加监听
*
* @param l 监听器
*/
public void addLeadingAction(ActionListener l) {
newTemplateAction = l;
leadingActionButton.removeActionListener(newTemplateAction);
leadingActionButton.addActionListener(newTemplateAction);
}
public void setToolBarEnable(boolean enable) {
leadingToolBar.setEnabled(enable);
}
@ -787,7 +775,9 @@ public class MultiTemplateTabPane extends Row {
public void mouseClicked(MouseEvent e) {
// 双击添加模板
if (e.getClickCount() == DOUBLE_CLICK
&& isOverBlank(e.getX())) {
&& isOverBlank(e.getX())
&& newTemplateAction != null
) {
newTemplateAction.actionPerformed(null);
}
}
@ -1003,6 +993,7 @@ public class MultiTemplateTabPane extends Row {
/**
* 创建新建模板的前导区工具按钮
*
* @return
*/
private ToolBarDef createLeadingToolBarDef() {
@ -1013,17 +1004,21 @@ public class MultiTemplateTabPane extends Row {
/**
* 创建新建文件的按钮数组
*
* @return
*/
public ShortCut[] createNewTemplateShortCuts() {
ArrayList<ShortCut> shortCuts = new ArrayList<ShortCut>();
shortCuts.add(new NewWorkBookToolButtonAction());
NewWorkBookToolButtonAction action = new NewWorkBookToolButtonAction();
shortCuts.add(action);
newTemplateAction = action;
return shortCuts.toArray(new ShortCut[0]);
}
/**
* 插入插件中的按钮
*
* @return
*/
protected void insertLeadingToolButton(ToolBarDef toolBarDef, String anchor) {

21
designer-base/src/main/java/com/fr/design/gui/itree/refreshabletree/RefreshableJTree.java

@ -108,17 +108,18 @@ public abstract class RefreshableJTree extends CheckBoxTree {
for (int i = 0; i < nodes.length; i++) {
treeNode.add(nodes[i]);
}
DefaultTreeModel treeModel = (DefaultTreeModel) RefreshableJTree.this.getModel();
// 主要耗时是用在了treeUI的渲染上了,所以把这个放到工作线程里面
if (treeNode.getChildCount() >= 1 && ((ExpandMutableTreeNode) treeNode.getFirstChild()).getUserObject() == PENDING) {
treeNode.remove(0);
}
treeModel.nodeStructureChanged(treeNode);
return System.currentTimeMillis() - startTime;
}
@Override
protected void done() {
DefaultTreeModel treeModel = (DefaultTreeModel) RefreshableJTree.this.getModel();
treeModel.nodeStructureChanged(treeNode);
RefreshableJTree.this.updateUI();
// 恢复Tree的可用性
RefreshableJTree.this.setEnabled(true);
@ -315,4 +316,20 @@ public abstract class RefreshableJTree extends CheckBoxTree {
tip.setOpaque(false);
return tip;
}
@Override
public TreePath getPathForLocation(int x, int y) {
// NewUI树组件全选行,仅考虑y坐标即可
TreePath closestPath = getClosestPathForLocation(x, y);
if (closestPath != null) {
Rectangle pathBounds = getPathBounds(closestPath);
if(pathBounds != null &&
y >= pathBounds.y && y < (pathBounds.y + pathBounds.height)) {
return closestPath;
}
}
return null;
}
}

56
designer-base/src/main/java/com/fr/design/i18n/DesignExtendLanguageConfig.java

@ -0,0 +1,56 @@
package com.fr.design.i18n;
import com.fr.config.ConfigContext;
import com.fr.config.DefaultConfiguration;
import com.fr.config.Identifier;
import com.fr.config.holder.factory.Holders;
import com.fr.config.holder.impl.MapConf;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
/**
* 设计器语言扩展配置
*
* @author obo
* @since 11.0
* Created on 2024/09/26
*/
public class DesignExtendLanguageConfig extends DefaultConfiguration {
private static volatile DesignExtendLanguageConfig designExtendLanguageConfig = null;
/**
* 获取实例
*/
public static DesignExtendLanguageConfig getInstance() {
if (designExtendLanguageConfig == null) {
designExtendLanguageConfig = ConfigContext.getConfigInstance(DesignExtendLanguageConfig.class);
}
return designExtendLanguageConfig;
}
/**
* 设计器扩展的语言
* key为localeString例如en_US或envalue为改语言对应的国际化翻译key
*/
@Identifier("extendDesignLocales")
private MapConf<Map<String, String>> extendDesignLocales = Holders.map(new HashMap<>(), String.class, String.class);
public Map<String, String> getExtendedDesignLocales() {
return Collections.unmodifiableMap(extendDesignLocales.get());
}
public void setExtendedDesignLocales(Map<String, String> map) {
extendDesignLocales.set(map);
}
@Override
public Object clone() throws CloneNotSupportedException {
DesignExtendLanguageConfig cloned = (DesignExtendLanguageConfig) super.clone();
cloned.extendDesignLocales = ( MapConf<Map<String, String>>) extendDesignLocales.clone();
return cloned;
}
}

3
designer-base/src/main/java/com/fr/design/mainframe/CenterRegionContainerPane.java

@ -98,8 +98,7 @@ public class CenterRegionContainerPane extends JPanel {
}
private MultiTemplateTabPane initTemplateTabPane() {
MultiTemplateTabPane templateTabPane = MultiTemplateTabPane.getInstance();
return templateTabPane;
return MultiTemplateTabPane.getInstance();
}
public ToolBarMenuDock getToolBarMenuDock() {

6
designer-base/src/main/java/com/fr/design/mainframe/JTemplate.java

@ -2049,8 +2049,10 @@ public abstract class JTemplate<T extends BaseBook, U extends BaseUndoState<?>>
themeAttrMark = new TemplateThemeAttrMark();
getTarget().addAttrMark(themeAttrMark);
}
themeAttrMark.setName(theme.getName());
themeAttrMark.setDark(theme.isDark());
if (theme != null) {
themeAttrMark.setName(theme.getName());
themeAttrMark.setDark(theme.isDark());
}
}
public String getTemplateOpenFailedTip() {

19
designer-base/src/main/java/com/fr/design/utils/DesignUtils.java

@ -13,6 +13,7 @@ import com.fr.design.deeplink.DeepLinkCore;
import com.fr.design.dialog.FineJOptionPane;
import com.fr.design.fun.DefaultValueAdjustProvider;
import com.fr.design.fun.DesignerEnvProcessor;
import com.fr.design.i18n.DesignExtendLanguageConfig;
import com.fr.design.i18n.Toolkit;
import com.fr.design.mainframe.DesignerContext;
import com.fr.design.ui.util.UIUtil;
@ -21,10 +22,13 @@ import com.fr.file.FileFILE;
import com.fr.general.ComparatorUtils;
import com.fr.general.FRFont;
import com.fr.general.GeneralContext;
import com.fr.general.LocaleType;
import com.fr.general.SupportLocale;
import com.fr.log.FineLoggerFactory;
import com.fr.plugin.injectable.PluginModule;
import com.fr.stable.ArrayUtils;
import com.fr.stable.CommonCodeUtils;
import com.fr.stable.CommonUtils;
import com.fr.stable.StableUtils;
import com.fr.stable.StringUtils;
import com.fr.stable.bridge.ObjectHolder;
@ -52,7 +56,10 @@ import java.net.ServerSocket;
import java.net.Socket;
import java.net.URI;
import java.nio.charset.StandardCharsets;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
@ -491,4 +498,16 @@ public class DesignUtils {
return Utils.getAvailableFontFamilyNames4Report();
}
/**
* 获取设计器可用的开放的语言配置包括默认开放的五种语言和finedb扩展项
* @return Locale和翻译对应的key
*/
public static Map<Locale, String> getAvailableLanguages() {
Map<Locale, String> map = new LinkedHashMap<>(SupportLocale.getInstance().getLocaleMap());
map.remove(LocaleType.RU.getLocale());
for (Map.Entry<String, String> entry : DesignExtendLanguageConfig.getInstance().getExtendedDesignLocales().entrySet()) {
map.put(CommonUtils.stringToLocale(entry.getKey()), entry.getValue());
}
return Collections.unmodifiableMap(map);
}
}

22
designer-base/src/main/java/com/fr/design/write/submit/DBManipulationPane.java

@ -2,11 +2,9 @@ package com.fr.design.write.submit;
import com.fine.swing.ui.layout.Layouts;
import com.fine.theme.icon.LazyIcon;
import com.fine.swing.ui.layout.Spacer;
import com.fine.theme.light.ui.FineRoundBorder;
import com.fine.theme.light.ui.FineTableHeaderUI;
import com.fine.theme.utils.FineClientProperties;
import com.fine.theme.utils.FineUIScale;
import com.fine.theme.utils.FineUIUtils;
import com.fr.base.BaseFormula;
import com.fr.base.Parameter;
@ -21,7 +19,6 @@ import com.fr.design.constants.UIConstants;
import com.fr.design.data.datapane.ChoosePaneSupportFormula;
import com.fr.design.data.datapane.DataBaseItems;
import com.fr.design.data.tabledata.tabledatapane.FormatExplanationPane;
import com.fr.design.dialog.AttrScrollPane;
import com.fr.design.dialog.BasicDialog;
import com.fr.design.dialog.BasicPane;
import com.fr.design.dialog.DialogActionAdapter;
@ -33,7 +30,6 @@ 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.icombobox.UIComboBoxRenderer;
import com.fr.design.gui.icontainer.UIScrollPane;
import com.fr.design.gui.icontainer.UIVerticalScrollPane;
import com.fr.design.gui.ilable.UILabel;
import com.fr.design.gui.ilist.CheckBoxList;
@ -50,6 +46,7 @@ import com.fr.general.data.Condition;
import com.fr.log.FineLoggerFactory;
import com.fr.stable.ColumnRow;
import com.fr.stable.ColumnRowGroup;
import com.fr.stable.StringUtils;
import com.fr.write.DBManipulation;
import com.fr.write.NameSubmitJob;
import com.fr.write.config.ColumnConfig;
@ -103,7 +100,6 @@ import java.util.stream.Collectors;
import static com.fine.swing.ui.layout.Layouts.row;
import static com.fine.swing.ui.layout.Layouts.cell;
import static com.fine.swing.ui.layout.Layouts.column;
import static com.fine.swing.ui.layout.Layouts.row;
import static com.fine.theme.utils.FineUIScale.scale;
//august:这个东西应该分成两类,一类是有单元格的情况,一类是没有单元格的情况
@ -1225,6 +1221,14 @@ public class DBManipulationPane extends BasicBeanPane<DBManipulation> {
public boolean checkSelected(boolean isSelected, Class columnClass) {
return isSelected;
}
@Override
public String getTooltipValue(Object value) {
if (value instanceof ColumnName) {
return ((ColumnName) value).name;
}
return StringUtils.BLANK;
}
}
/*
@ -1255,6 +1259,14 @@ public class DBManipulationPane extends BasicBeanPane<DBManipulation> {
public boolean checkSelected(boolean isSelected, Class columnClass) {
return isSelected;
}
@Override
public String getTooltipValue(Object value) {
if (value instanceof ColumnValue) {
return ((ColumnValue) value).obj.toString();
}
return StringUtils.BLANK;
}
}
/**

23
designer-base/src/main/java/com/fr/file/FILEChooserPane.java

@ -1926,15 +1926,15 @@ public class FILEChooserPane extends BasicPane {
nameField.getDocument().addDocumentListener(new DocumentListener() {
public void changedUpdate(DocumentEvent e) {
validInput();
resetInputChecker();
}
public void insertUpdate(DocumentEvent e) {
validInput();
resetInputChecker();
}
public void removeUpdate(DocumentEvent e) {
validInput();
resetInputChecker();
}
});
nameField.selectAll();
@ -1993,7 +1993,9 @@ public class FILEChooserPane extends BasicPane {
}
private void confirmClose() {
if (!validInput()) {
return;
};
String userInput = nameField.getText().trim();
// 处理不合法的文件夹名称
@ -2028,12 +2030,13 @@ public class FILEChooserPane extends BasicPane {
}
private void validInput() {
private boolean validInput() {
boolean valid = true;
String userInput = nameField.getText().trim();
if (StringUtils.isEmpty(userInput)) {
confirmButton.setEnabled(false);
valid = false;
}
boolean duplicate = false;
@ -2045,7 +2048,7 @@ public class FILEChooserPane extends BasicPane {
break;
}
}
valid = valid && !duplicate;
if (duplicate) {
nameField.selectAll();
// 如果文件名已存在,则灰掉确认按钮
@ -2059,6 +2062,12 @@ public class FILEChooserPane extends BasicPane {
warnLabel.setVisible(false);
confirmButton.setEnabled(true);
}
return valid;
}
private void resetInputChecker() {
warnLabel.setVisible(false);
confirmButton.setEnabled(true);
}
}
}

13
designer-base/src/main/resources/com/fine/theme/icon/editor/cell_group_popup.svg

@ -0,0 +1,13 @@
<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_13814_85902)">
<path fill-rule="evenodd" clip-rule="evenodd" d="M1.90625 3.53125C1.90625 2.53023 2.71773 1.71875 3.71875 1.71875H14.5V3.53125H3.71875V13.5H1.90625V3.53125ZM25.2812 23.4688V13.5H27.0938V23.4688C27.0938 24.4698 26.2823 25.2812 25.2812 25.2812H14.5V23.4688H25.2812Z" fill="#0A1C38" fill-opacity="0.9"/>
<path d="M1.90625 13.5H14.5V25.2812H3.71875C2.71773 25.2812 1.90625 24.4698 1.90625 23.4688V13.5Z" fill="#2576EF"/>
<path d="M27.0938 13.5H14.5V1.71875H25.2812C26.2823 1.71875 27.0938 2.53023 27.0938 3.53125V13.5Z" fill="#2576EF"/>
<path d="M24.2851 30C24.0316 30 23.9047 29.6935 24.0839 29.5143L29.5143 24.0839C29.6935 23.9047 30 24.0316 30 24.2851V29.6C30 29.8209 29.8209 30 29.6 30H24.2851Z" fill="#0A1C38" fill-opacity="0.9"/>
</g>
<defs>
<clipPath id="clip0_13814_85902">
<rect width="32" height="32" fill="white"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 985 B

13
designer-base/src/main/resources/com/fine/theme/icon/editor/cell_group_popup_disable.svg

@ -0,0 +1,13 @@
<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_13814_85916)">
<path fill-rule="evenodd" clip-rule="evenodd" d="M1.90625 3.53125C1.90625 2.53023 2.71773 1.71875 3.71875 1.71875H14.5V3.53125H3.71875V13.5H1.90625V3.53125ZM25.2812 23.4688V13.5H27.0938V23.4688C27.0938 24.4698 26.2823 25.2812 25.2812 25.2812H14.5V23.4688H25.2812Z" fill="#0A1C38" fill-opacity="0.29"/>
<path d="M1.90625 13.5H14.5V25.2812H3.71875C2.71773 25.2812 1.90625 24.4698 1.90625 23.4688V13.5Z" fill="#0A1C38" fill-opacity="0.29"/>
<path d="M27.0938 13.5H14.5V1.71875H25.2812C26.2823 1.71875 27.0938 2.53023 27.0938 3.53125V13.5Z" fill="#0A1C38" fill-opacity="0.29"/>
<path d="M24.2851 30C24.0316 30 23.9047 29.6935 24.0839 29.5143L29.5143 24.0839C29.6935 23.9047 30 24.0316 30 24.2851V29.6C30 29.8209 29.8209 30 29.6 30H24.2851Z" fill="#0A1C38" fill-opacity="0.29"/>
</g>
<defs>
<clipPath id="clip0_13814_85916">
<rect width="32" height="32" fill="white"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 1.0 KiB

1
designer-base/src/main/resources/com/fine/theme/light/ui/fine_light.icon.json

@ -393,6 +393,7 @@
"date_popup": "editor/date_popup.svg",
"param_popup": "editor/param_popup.svg",
"cellelement_popup": "editor/cellelement_popup.svg",
"cell_group_popup": "editor/cell_group_popup.svg",
"bind_column_popup": "editor/bind_column_popup.svg",
"preview_mobile": "preview/preview_mobile.svg",
"preview_paging": "preview/preview_paging.svg",

120
designer-base/src/main/resources/com/fr/design/data/tabledata/datacenter/web/data-choose.main.js

File diff suppressed because one or more lines are too long

2
designer-base/src/main/resources/com/fr/design/i18n/dimension_en.properties

@ -39,6 +39,6 @@ com.fr.design.actions.printingSetting.dialog=700*600
com.fr.design.data.datapane.preview.dialog=700*600
com.fr.design.data.tabledata.dialog=1200*600
com.fr.design.dscolumn.SpecifiedGroupAttrPane=900*600
com.fr.design.data.datapane.preview.PreviewTablePane=800*520
com.fr.design.data.datapane.preview.PreviewTablePane=900*520

2
designer-realize/src/main/java/com/fanruan/boot/env/function/app/DesignAppComponent.java vendored

@ -10,7 +10,6 @@ import com.fr.design.mainframe.App;
import com.fr.design.mainframe.AppGroup;
import com.fr.design.mainframe.JTemplateFactory;
import com.fr.nx.app.designer.CptxApp;
import com.fr.workspace.server.repository.authority.RemoteAuthorityRepository;
import java.util.ArrayList;
import java.util.List;
@ -44,7 +43,6 @@ public class DesignAppComponent {
*/
@Stop
public void stop() {
RemoteAuthorityRepository.getInstance().invalidCache();
List<App> appList = new ArrayList<>(Carina.getApplicationContext().group(AppGroup.class).getAll());
for (App app : appList) {
JTemplateFactory.remove(app);

4
designer-realize/src/main/java/com/fr/start/MainDesigner.java

@ -337,8 +337,8 @@ public class MainDesigner extends BaseDesigner {
}
private UICombinationButton createRunButton() {
run = new UICombinationButton(new UISaveForbiddenButton(Toolkit.i18nText("Fine-Design_Basic_Preview"), new LazyIcon("run").white()),
new UISaveForbiddenButton(new LazyIcon("triangle_down").white()));
run = new UICombinationButton(new UIButton(Toolkit.i18nText("Fine-Design_Basic_Preview"), new LazyIcon("run").white()),
new UIButton(new LazyIcon("triangle_down").white()));
run.addLeftClickLister(mouseEvent -> {
JTemplate<?, ?> jt = HistoryTemplateListCache.getInstance().getCurrentEditingTemplate();
if (jt == null || jt.isSaving()) {

Loading…
Cancel
Save