@ -0,0 +1,24 @@ |
|||||||
|
package com.fr.design.base.clipboard; |
||||||
|
|
||||||
|
import java.util.List; |
||||||
|
|
||||||
|
public class ClipboardHelper { |
||||||
|
public static String formatExcelString(List<List<Object>> table) { |
||||||
|
StringBuffer stringBuffer = new StringBuffer(); |
||||||
|
|
||||||
|
for (int row = 0; row < table.size(); row++) { |
||||||
|
List<Object> rowValue = table.get(row); |
||||||
|
for (int col = 0; col < rowValue.size(); col++) { |
||||||
|
Object cell = rowValue.get(col); |
||||||
|
stringBuffer.append(cell); |
||||||
|
if (col != rowValue.size() - 1) { |
||||||
|
stringBuffer.append("\t"); |
||||||
|
} |
||||||
|
} |
||||||
|
if (row != table.size() - 1) { |
||||||
|
stringBuffer.append("\n"); |
||||||
|
} |
||||||
|
} |
||||||
|
return stringBuffer.toString(); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,203 @@ |
|||||||
|
package com.fr.design.data.datapane.preview; |
||||||
|
|
||||||
|
import com.fr.design.base.clipboard.ClipboardHelper; |
||||||
|
import com.fr.design.gui.itable.SortableJTable; |
||||||
|
import com.fr.design.gui.itable.TableSorter; |
||||||
|
import com.fr.log.FineLoggerFactory; |
||||||
|
import com.fr.stable.os.OperatingSystem; |
||||||
|
|
||||||
|
import javax.swing.table.TableCellRenderer; |
||||||
|
import java.awt.*; |
||||||
|
import java.awt.datatransfer.Clipboard; |
||||||
|
import java.awt.datatransfer.StringSelection; |
||||||
|
import java.awt.datatransfer.Transferable; |
||||||
|
import java.awt.event.KeyAdapter; |
||||||
|
import java.awt.event.KeyEvent; |
||||||
|
import java.awt.event.MouseAdapter; |
||||||
|
import java.awt.event.MouseEvent; |
||||||
|
import java.util.ArrayList; |
||||||
|
import java.util.Collections; |
||||||
|
import java.util.Comparator; |
||||||
|
|
||||||
|
public class CopyableJTable extends SortableJTable { |
||||||
|
|
||||||
|
//区域选中用到的定位数据
|
||||||
|
public int startRow = -1; |
||||||
|
public int startCol = -1; |
||||||
|
public int endRow = -1; |
||||||
|
public int endCol = -1; |
||||||
|
//单元格不连续多选用到的定位数据
|
||||||
|
java.util.List<Point> pointList = new ArrayList<>(); |
||||||
|
//shift键是否被按下
|
||||||
|
public boolean isShiftDown = false; |
||||||
|
//control\command键是否被按下
|
||||||
|
public boolean isControlDown = false; |
||||||
|
//是否可以复制
|
||||||
|
public boolean isCopy = true; |
||||||
|
int ctrlKeyCode = 17; |
||||||
|
int cKeyCode = 67; |
||||||
|
int shiftKeyCode = 16; |
||||||
|
int commandKeyCode = 157; |
||||||
|
//选中单元格的背景色
|
||||||
|
Color selectBackGround = new Color(54, 133, 242, 63); |
||||||
|
|
||||||
|
|
||||||
|
public CopyableJTable(TableSorter tableModel) { |
||||||
|
super(tableModel); |
||||||
|
initListener(); |
||||||
|
} |
||||||
|
|
||||||
|
private void initListener() { |
||||||
|
CopyableJTable self = this; |
||||||
|
this.addMouseMotionListener(new java.awt.event.MouseAdapter() { |
||||||
|
@Override |
||||||
|
public void mouseDragged(MouseEvent evt) { |
||||||
|
int row = self.rowAtPoint(evt.getPoint()); |
||||||
|
int col = self.columnAtPoint(evt.getPoint()); |
||||||
|
if (self.updateEndPoint(row, col)) { |
||||||
|
self.repaint(); |
||||||
|
} |
||||||
|
} |
||||||
|
}); |
||||||
|
this.addMouseListener(new MouseAdapter() { |
||||||
|
public void mousePressed(MouseEvent e) { |
||||||
|
int row = self.rowAtPoint(e.getPoint()); |
||||||
|
int col = self.columnAtPoint(e.getPoint()); |
||||||
|
if (!self.isControlDown) { |
||||||
|
self.clearPoint(); |
||||||
|
} |
||||||
|
if (self.isShiftDown) { |
||||||
|
self.clearPoint(); |
||||||
|
} else { |
||||||
|
self.updateStartPoint(row, col); |
||||||
|
} |
||||||
|
self.addPoint(row, col); |
||||||
|
self.updateEndPoint(row, col); |
||||||
|
|
||||||
|
self.repaint(); |
||||||
|
} |
||||||
|
|
||||||
|
}); |
||||||
|
this.addKeyListener(new KeyAdapter() { |
||||||
|
@Override |
||||||
|
public void keyPressed(KeyEvent e) { |
||||||
|
if (isControlKey(e)) { |
||||||
|
isControlDown = true; |
||||||
|
} else if (e.getKeyCode() == shiftKeyCode) { |
||||||
|
isShiftDown = true; |
||||||
|
} else if (e.getKeyCode() == cKeyCode) { |
||||||
|
if (isControlDown && isCopy) { |
||||||
|
self.copy(); |
||||||
|
isCopy = false; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void keyReleased(KeyEvent e) { |
||||||
|
if (isControlKey(e)) { |
||||||
|
isControlDown = false; |
||||||
|
isCopy = true; |
||||||
|
} else if (e.getKeyCode() == shiftKeyCode) { |
||||||
|
isShiftDown = false; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private boolean isControlKey(KeyEvent e) { |
||||||
|
if (e.getKeyCode() == ctrlKeyCode) { |
||||||
|
return true; |
||||||
|
} |
||||||
|
if (e.getKeyCode() == commandKeyCode && OperatingSystem.isMacos()) { |
||||||
|
return true; |
||||||
|
} |
||||||
|
return false; |
||||||
|
} |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
@Override |
||||||
|
public Component prepareRenderer(TableCellRenderer renderer, int row, int column) { |
||||||
|
Component comp = super.prepareRenderer(renderer, row, column); |
||||||
|
if (isChoose(row, column)) { |
||||||
|
comp.setBackground(selectBackGround); |
||||||
|
} else { |
||||||
|
comp.setBackground(this.getBackground()); |
||||||
|
} |
||||||
|
return comp; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
private boolean updateEndPoint(int row, int col) { |
||||||
|
if (endRow != row || endCol != col) { |
||||||
|
endRow = row; |
||||||
|
endCol = col; |
||||||
|
return true; |
||||||
|
} |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
private boolean updateStartPoint(int row, int col) { |
||||||
|
if (startRow != row || startCol != col) { |
||||||
|
startRow = row; |
||||||
|
startCol = col; |
||||||
|
return true; |
||||||
|
} |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
private void addPoint(int row, int col) { |
||||||
|
pointList.add(new Point(row, col)); |
||||||
|
} |
||||||
|
|
||||||
|
private void clearPoint() { |
||||||
|
pointList = new ArrayList<>(); |
||||||
|
} |
||||||
|
|
||||||
|
private void copy() { |
||||||
|
FineLoggerFactory.getLogger().info("copy cell value"); |
||||||
|
java.util.List<java.util.List<Object>> table = new ArrayList<>(); |
||||||
|
if ((startRow != endRow || startCol != endCol) && |
||||||
|
Math.min(startRow, endRow) > -1 && Math.min(startCol, endCol) > -1) { |
||||||
|
for (int i = Math.min(startRow, endRow); i <= Math.max(startRow, endRow); i++) { |
||||||
|
table.add(new ArrayList<>()); |
||||||
|
for (int j = Math.min(startCol, endCol); j <= Math.max(startCol, endCol); j++) { |
||||||
|
Object text = this.getValueAt(i, j); |
||||||
|
table.get(i - Math.min(startRow, endRow)).add(text); |
||||||
|
} |
||||||
|
} |
||||||
|
} else if (pointList.size() > 0) { |
||||||
|
Collections.sort(pointList, Comparator.comparing(Point::getX).thenComparing(Point::getY)); |
||||||
|
int startRow = pointList.get(0).x; |
||||||
|
int currentRow = startRow; |
||||||
|
table.add(new ArrayList<>()); |
||||||
|
for (Point point : pointList) { |
||||||
|
while (currentRow < point.x) { |
||||||
|
table.add(new ArrayList<>()); |
||||||
|
currentRow++; |
||||||
|
} |
||||||
|
Object text = this.getValueAt(point.x, point.y); |
||||||
|
table.get(currentRow - startRow).add(text); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
Clipboard clip = Toolkit.getDefaultToolkit().getSystemClipboard(); |
||||||
|
Transferable tText = new StringSelection(ClipboardHelper.formatExcelString(table)); |
||||||
|
clip.setContents(tText, null); |
||||||
|
} |
||||||
|
|
||||||
|
private boolean isChoose(int row, int col) { |
||||||
|
if (row >= Math.min(startRow, endRow) && row <= Math.max(startRow, endRow)) { |
||||||
|
if (col >= Math.min(startCol, endCol) && col <= Math.max(startCol, endCol)) { |
||||||
|
return true; |
||||||
|
} |
||||||
|
} |
||||||
|
for (Point point : pointList) { |
||||||
|
if (point.x == row && point.y == col) { |
||||||
|
return true; |
||||||
|
} |
||||||
|
} |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,16 @@ |
|||||||
|
package com.fr.design.gui.chart; |
||||||
|
|
||||||
|
import com.fr.chart.chartattr.ChartCollection; |
||||||
|
|
||||||
|
import java.util.EventListener; |
||||||
|
|
||||||
|
/** |
||||||
|
* @author shine |
||||||
|
* @version 10.0 |
||||||
|
* Created by shine on 2021/5/26 |
||||||
|
*/ |
||||||
|
public interface ChartEditPaneActionListener extends EventListener { |
||||||
|
|
||||||
|
void attributeChange(ChartCollection chartCollection); |
||||||
|
|
||||||
|
} |
@ -0,0 +1,63 @@ |
|||||||
|
package com.fr.design.gui.ibutton; |
||||||
|
|
||||||
|
import com.fr.stable.StringUtils; |
||||||
|
|
||||||
|
import javax.swing.Icon; |
||||||
|
|
||||||
|
public class UIHead { |
||||||
|
private String text = StringUtils.EMPTY; |
||||||
|
private Icon icon = null; |
||||||
|
private boolean enable = true; |
||||||
|
private int index = 0; |
||||||
|
|
||||||
|
public UIHead(String text, int index) { |
||||||
|
this.text = text; |
||||||
|
this.index = index; |
||||||
|
} |
||||||
|
|
||||||
|
public UIHead(String text, int index, boolean enable) { |
||||||
|
this(text, index); |
||||||
|
this.enable = enable; |
||||||
|
} |
||||||
|
|
||||||
|
public UIHead(Icon icon, int index) { |
||||||
|
this.icon = icon; |
||||||
|
this.index = index; |
||||||
|
} |
||||||
|
|
||||||
|
public UIHead(Icon icon, int index, boolean enable) { |
||||||
|
this(icon, index); |
||||||
|
this.enable = enable; |
||||||
|
} |
||||||
|
|
||||||
|
public UIHead(String text, Icon icon, int index) { |
||||||
|
this.text = text; |
||||||
|
this.icon = icon; |
||||||
|
this.index = index; |
||||||
|
} |
||||||
|
|
||||||
|
public UIHead(String text, Icon icon, int index, boolean enable) { |
||||||
|
this(text, icon, index); |
||||||
|
this.enable = enable; |
||||||
|
} |
||||||
|
|
||||||
|
public boolean isOnlyText() { |
||||||
|
return StringUtils.isNotEmpty(text) && icon == null; |
||||||
|
} |
||||||
|
|
||||||
|
public String getText() { |
||||||
|
return text; |
||||||
|
} |
||||||
|
|
||||||
|
public Icon getIcon() { |
||||||
|
return icon; |
||||||
|
} |
||||||
|
|
||||||
|
public boolean isEnable() { |
||||||
|
return enable; |
||||||
|
} |
||||||
|
|
||||||
|
public int getIndex() { |
||||||
|
return index; |
||||||
|
} |
||||||
|
} |
@ -1,103 +0,0 @@ |
|||||||
package com.fr.design.gui.ifilechooser; |
|
||||||
|
|
||||||
import javax.swing.filechooser.FileFilter; |
|
||||||
import java.awt.*; |
|
||||||
import java.io.File; |
|
||||||
|
|
||||||
/** |
|
||||||
* @author hades |
|
||||||
* @version 10.0 |
|
||||||
* Created by hades on 2020/3/31 |
|
||||||
*/ |
|
||||||
public abstract class AbstractFileChooser { |
|
||||||
|
|
||||||
/** |
|
||||||
* 返回当前目录 |
|
||||||
* |
|
||||||
*/ |
|
||||||
public abstract File getCurrentDirectory(); |
|
||||||
|
|
||||||
/** |
|
||||||
* 返回当前的文件选择过滤器 |
|
||||||
* |
|
||||||
*/ |
|
||||||
public abstract FileFilter getFileFilter(); |
|
||||||
|
|
||||||
/** |
|
||||||
* 返回选择的文件 |
|
||||||
* |
|
||||||
*/ |
|
||||||
public abstract File getSelectedFile(); |
|
||||||
|
|
||||||
/** |
|
||||||
* 多文件选择模式下 返回选择的多个文件 |
|
||||||
* |
|
||||||
*/ |
|
||||||
public abstract File[] getSelectedFiles(); |
|
||||||
|
|
||||||
/** |
|
||||||
* 是否可以选择多个文件 |
|
||||||
* |
|
||||||
*/ |
|
||||||
public abstract boolean isMultiSelectionEnabled(); |
|
||||||
|
|
||||||
/** |
|
||||||
* 设置当前选择的目录 |
|
||||||
* |
|
||||||
*/ |
|
||||||
public abstract void setCurrentDirectory(File dir); |
|
||||||
|
|
||||||
/** |
|
||||||
* 设置左上角标题 |
|
||||||
* |
|
||||||
*/ |
|
||||||
public abstract void setDialogTitle(String title); |
|
||||||
|
|
||||||
/** |
|
||||||
* 设置当前的文件过滤器 |
|
||||||
* |
|
||||||
*/ |
|
||||||
public abstract void setFileFilter(final FileFilter filter); |
|
||||||
|
|
||||||
/** |
|
||||||
* 设置文件选择器模式 |
|
||||||
* |
|
||||||
* JFileChooser.FILES_ONLY |
|
||||||
* JFileChooser.DIRECTORIES_ONLY |
|
||||||
* JFileChooser.FILES_AND_DIRECTORIES |
|
||||||
*/ |
|
||||||
public abstract void setFileSelectionMode(int selectionMode); |
|
||||||
|
|
||||||
/** |
|
||||||
* 设置是否允许选择多个文件 |
|
||||||
* |
|
||||||
*/ |
|
||||||
public abstract void setMultiSelectionEnabled(boolean multiple); |
|
||||||
|
|
||||||
/** |
|
||||||
* 设置选择的文件 用于showSaveDialog |
|
||||||
* |
|
||||||
*/ |
|
||||||
public abstract void setSelectedFile(File file); |
|
||||||
|
|
||||||
/** |
|
||||||
* 弹出文件选择器 打开文件 |
|
||||||
* |
|
||||||
*/ |
|
||||||
public abstract int showOpenDialog(Component parent); |
|
||||||
|
|
||||||
/** |
|
||||||
* 弹出文件选择器 保存文件 |
|
||||||
* |
|
||||||
*/ |
|
||||||
public abstract int showSaveDialog(Component parent); |
|
||||||
|
|
||||||
|
|
||||||
/** |
|
||||||
* https://bugs.java.com/bugdatabase/view_bug.do?bug_id=4031440
|
|
||||||
* |
|
||||||
* 设置文件名后缀 起到文件过滤的作用 形如 "*.jpg;*.jpeg" |
|
||||||
* |
|
||||||
*/ |
|
||||||
public abstract void setExtensionFilter(String file); |
|
||||||
} |
|
@ -0,0 +1,12 @@ |
|||||||
|
package com.fr.design.gui.ifilechooser; |
||||||
|
|
||||||
|
import java.awt.*; |
||||||
|
import java.io.File; |
||||||
|
|
||||||
|
public interface FileChooserProvider { |
||||||
|
File[] getSelectedFiles(); |
||||||
|
|
||||||
|
File getSelectedFile(); |
||||||
|
|
||||||
|
int showDialog(Component parent); |
||||||
|
} |
@ -0,0 +1,5 @@ |
|||||||
|
package com.fr.design.gui.ifilechooser; |
||||||
|
|
||||||
|
public enum FileSelectionMode { |
||||||
|
FILE, MULTIPLE_FILE, DIR |
||||||
|
} |
@ -0,0 +1,158 @@ |
|||||||
|
package com.fr.design.gui.ifilechooser; |
||||||
|
|
||||||
|
|
||||||
|
import com.fr.design.i18n.Toolkit; |
||||||
|
import com.fr.design.upm.UpmUtils; |
||||||
|
import com.fr.log.FineLoggerFactory; |
||||||
|
import com.fr.stable.StringUtils; |
||||||
|
import com.sun.javafx.application.PlatformImpl; |
||||||
|
import javafx.stage.DirectoryChooser; |
||||||
|
import javafx.stage.FileChooser; |
||||||
|
import javafx.stage.Window; |
||||||
|
|
||||||
|
import javax.swing.*; |
||||||
|
import javax.swing.filechooser.FileNameExtensionFilter; |
||||||
|
import java.awt.*; |
||||||
|
import java.io.File; |
||||||
|
import java.util.List; |
||||||
|
import java.util.concurrent.CountDownLatch; |
||||||
|
|
||||||
|
public class JavaFxNativeFileChooser implements FileChooserProvider { |
||||||
|
private File[] selectedFiles = new File[0]; |
||||||
|
private FileSelectionMode fileSelectionMode = FileSelectionMode.FILE; |
||||||
|
private String title = Toolkit.i18nText("Fine-Design_Basic_Open"); |
||||||
|
private FileChooser.ExtensionFilter[] filters; |
||||||
|
private File currentDirectory; |
||||||
|
|
||||||
|
@Override |
||||||
|
public File[] getSelectedFiles() { |
||||||
|
return selectedFiles; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public File getSelectedFile() { |
||||||
|
if (selectedFiles.length > 0) { |
||||||
|
return selectedFiles[0]; |
||||||
|
} |
||||||
|
return null; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public int showDialog(Component parent) { |
||||||
|
final CountDownLatch latch = new CountDownLatch(1); |
||||||
|
PlatformImpl.startup(new Runnable() { |
||||||
|
@Override |
||||||
|
public void run() { |
||||||
|
try { |
||||||
|
if (fileSelectionMode == FileSelectionMode.FILE || fileSelectionMode == FileSelectionMode.MULTIPLE_FILE) { |
||||||
|
FileChooser fileChooser = new FileChooser(); |
||||||
|
fileChooser.setTitle(title); |
||||||
|
fileChooser.getExtensionFilters().addAll(filters); |
||||||
|
fileChooser.setInitialDirectory(currentDirectory); |
||||||
|
if (fileSelectionMode == FileSelectionMode.FILE) { |
||||||
|
File file = fileChooser.showOpenDialog(null); |
||||||
|
if (file != null) { |
||||||
|
selectedFiles = new File[]{file}; |
||||||
|
} |
||||||
|
} else if (fileSelectionMode == FileSelectionMode.MULTIPLE_FILE) { |
||||||
|
List<File> fileList = fileChooser.showOpenMultipleDialog(null); |
||||||
|
if (fileList != null) { |
||||||
|
selectedFiles = new File[fileList.size()]; |
||||||
|
fileList.toArray(selectedFiles); |
||||||
|
} |
||||||
|
} |
||||||
|
} else if (fileSelectionMode == FileSelectionMode.DIR) { |
||||||
|
DirectoryChooser directoryChooser = new DirectoryChooser(); |
||||||
|
directoryChooser.setTitle(title); |
||||||
|
directoryChooser.setInitialDirectory(currentDirectory); |
||||||
|
File folder = directoryChooser.showDialog(null); |
||||||
|
if (folder != null) { |
||||||
|
selectedFiles = new File[]{folder}; |
||||||
|
} |
||||||
|
System.out.println(folder); |
||||||
|
} |
||||||
|
|
||||||
|
} catch (Exception e) { |
||||||
|
FineLoggerFactory.getLogger().error(e, e.getMessage()); |
||||||
|
} finally { |
||||||
|
latch.countDown(); |
||||||
|
} |
||||||
|
} |
||||||
|
}); |
||||||
|
|
||||||
|
try { |
||||||
|
latch.await(); |
||||||
|
} catch (InterruptedException ignore) { |
||||||
|
} |
||||||
|
return selectedFiles.length > 0 ? JFileChooser.APPROVE_OPTION : JFileChooser.CANCEL_OPTION; |
||||||
|
} |
||||||
|
|
||||||
|
public void setSelectionMode(FileSelectionMode fileSelectionMode) { |
||||||
|
this.fileSelectionMode = fileSelectionMode; |
||||||
|
} |
||||||
|
|
||||||
|
public void setCurrentDirectory(File currentDirectory) { |
||||||
|
this.currentDirectory = currentDirectory; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
public static class Builder { |
||||||
|
private FileSelectionMode fileSelectionMode = FileSelectionMode.FILE; |
||||||
|
private String title = Toolkit.i18nText("Fine-Design_Basic_Open"); |
||||||
|
private FileChooser.ExtensionFilter[] filters = new FileChooser.ExtensionFilter[0]; |
||||||
|
private File currentDirectory; |
||||||
|
|
||||||
|
public Builder fileSelectionMode(FileSelectionMode fileSelectionMode) { |
||||||
|
this.fileSelectionMode = fileSelectionMode; |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
public Builder title(String title) { |
||||||
|
this.title = title; |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
public Builder filters(FileChooser.ExtensionFilter[] filters) { |
||||||
|
this.filters = filters; |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
public Builder filter(String des, String... extensions) { |
||||||
|
if (extensions != null) { |
||||||
|
this.filters = new FileChooser.ExtensionFilter[]{new FileChooser.ExtensionFilter(des, extensions)}; |
||||||
|
} |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
public Builder currentDirectory(File currentDirectory) { |
||||||
|
if (currentDirectory != null) { |
||||||
|
if (!currentDirectory.isDirectory()) { |
||||||
|
currentDirectory = currentDirectory.getParentFile(); |
||||||
|
} |
||||||
|
if (currentDirectory != null && currentDirectory.isDirectory()) { |
||||||
|
this.currentDirectory = currentDirectory; |
||||||
|
} |
||||||
|
} |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
public Builder currentDirectory(String path) { |
||||||
|
if (path != null) { |
||||||
|
return currentDirectory(new File(path)); |
||||||
|
} |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
public JavaFxNativeFileChooser build() { |
||||||
|
return new JavaFxNativeFileChooser(this); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private JavaFxNativeFileChooser(Builder builder) { |
||||||
|
this.fileSelectionMode = builder.fileSelectionMode; |
||||||
|
this.title = builder.title; |
||||||
|
this.filters = builder.filters; |
||||||
|
this.currentDirectory = builder.currentDirectory; |
||||||
|
} |
||||||
|
} |
@ -1,154 +0,0 @@ |
|||||||
package com.fr.design.gui.ifilechooser; |
|
||||||
|
|
||||||
import com.fr.design.gui.ifilechooser.AbstractFileChooser; |
|
||||||
import com.fr.design.mainframe.DesignerContext; |
|
||||||
import com.fr.stable.os.OperatingSystem; |
|
||||||
|
|
||||||
import javax.swing.*; |
|
||||||
import javax.swing.filechooser.FileFilter; |
|
||||||
import java.awt.*; |
|
||||||
import java.io.File; |
|
||||||
import java.io.FilenameFilter; |
|
||||||
|
|
||||||
|
|
||||||
/** |
|
||||||
* 系统原生风格的文件选择器 |
|
||||||
* |
|
||||||
* jdk问题: |
|
||||||
* https://bugs.openjdk.java.net/browse/JDK-4811090 不支持文件扩展选择
|
|
||||||
* https://stackoverflow.com/questions/40713398/filter-not-working-in-filedialog windows下 setFilenameFilter不work
|
|
||||||
* |
|
||||||
* @author hades |
|
||||||
* @version 10.0 |
|
||||||
* Created by hades on 2020/3/31 |
|
||||||
*/ |
|
||||||
public class UINativeFileChooser extends AbstractFileChooser { |
|
||||||
|
|
||||||
private final FileDialog fileDialog; |
|
||||||
private FileFilter fileFilter; |
|
||||||
private int selectionMode; |
|
||||||
|
|
||||||
public UINativeFileChooser(File file) { |
|
||||||
fileDialog = new FileDialog(DesignerContext.getDesignerFrame()); |
|
||||||
if (file != null) { |
|
||||||
fileDialog.setDirectory(file.getAbsolutePath()); |
|
||||||
fileDialog.setFile(file.toString()); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
public UINativeFileChooser() { |
|
||||||
this(null); |
|
||||||
} |
|
||||||
|
|
||||||
@Override |
|
||||||
public File getCurrentDirectory() { |
|
||||||
return new File(fileDialog.getDirectory()); |
|
||||||
} |
|
||||||
|
|
||||||
@Override |
|
||||||
public FileFilter getFileFilter() { |
|
||||||
return fileFilter; |
|
||||||
} |
|
||||||
|
|
||||||
@Override |
|
||||||
public File getSelectedFile() { |
|
||||||
return new File(fileDialog.getDirectory() + fileDialog.getFile()); |
|
||||||
} |
|
||||||
|
|
||||||
@Override |
|
||||||
public File[] getSelectedFiles() { |
|
||||||
return fileDialog.getFiles(); |
|
||||||
} |
|
||||||
|
|
||||||
@Override |
|
||||||
public boolean isMultiSelectionEnabled() { |
|
||||||
return fileDialog.isMultipleMode(); |
|
||||||
} |
|
||||||
|
|
||||||
@Override |
|
||||||
public void setCurrentDirectory(File f) { |
|
||||||
fileDialog.setDirectory(f.toString()); |
|
||||||
} |
|
||||||
|
|
||||||
@Override |
|
||||||
public void setDialogTitle(String title) { |
|
||||||
fileDialog.setTitle(title); |
|
||||||
} |
|
||||||
|
|
||||||
@Override |
|
||||||
public void setFileFilter(final FileFilter cff) { |
|
||||||
FilenameFilter filter = new FilenameFilter() { |
|
||||||
@Override |
|
||||||
public boolean accept(File Directory, String fileName) { |
|
||||||
return cff.accept(new File(Directory.getAbsolutePath() + fileName)); |
|
||||||
} |
|
||||||
}; |
|
||||||
fileDialog.setFilenameFilter(filter); |
|
||||||
fileFilter = cff; |
|
||||||
} |
|
||||||
|
|
||||||
@Override |
|
||||||
public void setFileSelectionMode(int selectionMode) { |
|
||||||
this.selectionMode = selectionMode; |
|
||||||
} |
|
||||||
|
|
||||||
@Override |
|
||||||
public void setMultiSelectionEnabled(boolean multiple) { |
|
||||||
fileDialog.setMultipleMode(multiple); |
|
||||||
} |
|
||||||
|
|
||||||
@Override |
|
||||||
public void setSelectedFile(File file) { |
|
||||||
fileDialog.setDirectory(file.getAbsolutePath()); |
|
||||||
fileDialog.setFile(file.getName()); |
|
||||||
} |
|
||||||
|
|
||||||
@Override |
|
||||||
public int showOpenDialog(Component parent) { |
|
||||||
boolean appleProperty = OperatingSystem.isMacos() && selectionMode == JFileChooser.DIRECTORIES_ONLY; |
|
||||||
if (appleProperty) { |
|
||||||
System.setProperty("apple.awt.fileDialogForDirectories", "true"); |
|
||||||
} |
|
||||||
try { |
|
||||||
fileDialog.setLocale(JComponent.getDefaultLocale()); |
|
||||||
fileDialog.setMode(FileDialog.LOAD); |
|
||||||
fileDialog.setVisible(true); |
|
||||||
return fileDialog.getFile() == null ? JFileChooser.CANCEL_OPTION : JFileChooser.APPROVE_OPTION; |
|
||||||
} finally { |
|
||||||
if (appleProperty) { |
|
||||||
System.setProperty("apple.awt.fileDialogForDirectories", "false"); |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
@Override |
|
||||||
public int showSaveDialog(Component parent) { |
|
||||||
fileDialog.setLocale(JComponent.getDefaultLocale()); |
|
||||||
fileDialog.setMode(FileDialog.SAVE); |
|
||||||
fileDialog.setVisible(true); |
|
||||||
return fileDialog.getFile() == null ? JFileChooser.CANCEL_OPTION : JFileChooser.APPROVE_OPTION; |
|
||||||
} |
|
||||||
|
|
||||||
@Override |
|
||||||
public void setExtensionFilter(String file) { |
|
||||||
fileDialog.setFile(file); |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* 确认本地文件选择器是否支持选择模式 |
|
||||||
* @param selectionMode 选择模式 |
|
||||||
* @return 是否支持选择模式 |
|
||||||
*/ |
|
||||||
public static boolean supportsSelectionMode(int selectionMode) { |
|
||||||
switch (selectionMode) { |
|
||||||
case JFileChooser.FILES_AND_DIRECTORIES: |
|
||||||
return false; |
|
||||||
case JFileChooser.DIRECTORIES_ONLY: |
|
||||||
return OperatingSystem.isMacos(); |
|
||||||
case JFileChooser.FILES_ONLY: |
|
||||||
default: |
|
||||||
return true; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
} |
|
@ -0,0 +1,278 @@ |
|||||||
|
package com.fr.design.mainframe; |
||||||
|
|
||||||
|
import com.fr.design.DesignState; |
||||||
|
import com.fr.design.base.mode.DesignModeContext; |
||||||
|
import com.fr.design.constants.UIConstants; |
||||||
|
import com.fr.design.file.HistoryTemplateListCache; |
||||||
|
import com.fr.design.file.MutilTempalteTabPane; |
||||||
|
import com.fr.design.file.NewTemplatePane; |
||||||
|
import com.fr.design.gui.ibutton.UIButton; |
||||||
|
import com.fr.design.gui.imenu.UIMenuHighLight; |
||||||
|
import com.fr.design.gui.itoolbar.UILargeToolbar; |
||||||
|
import com.fr.design.gui.itoolbar.UIToolbar; |
||||||
|
import com.fr.design.layout.FRGUIPaneFactory; |
||||||
|
import com.fr.design.mainframe.toolbar.ToolBarMenuDock; |
||||||
|
import com.fr.design.mainframe.toolbar.ToolBarMenuDockPlus; |
||||||
|
import org.jetbrains.annotations.Nullable; |
||||||
|
|
||||||
|
import javax.swing.JComponent; |
||||||
|
import javax.swing.JPanel; |
||||||
|
import javax.swing.border.MatteBorder; |
||||||
|
import java.awt.BorderLayout; |
||||||
|
import java.awt.Dimension; |
||||||
|
import java.awt.FlowLayout; |
||||||
|
import java.awt.Insets; |
||||||
|
import java.util.ArrayList; |
||||||
|
|
||||||
|
/** |
||||||
|
* @author shine |
||||||
|
* @version 10.0 |
||||||
|
* Created by shine on 2021/4/6 |
||||||
|
*/ |
||||||
|
public class CenterRegionContainerPane extends JPanel { |
||||||
|
|
||||||
|
private static volatile CenterRegionContainerPane THIS; |
||||||
|
|
||||||
|
private static final int LEFT_ALIGN_GAP = -5; |
||||||
|
|
||||||
|
private DesktopCardPane centerTemplateCardPane; |
||||||
|
|
||||||
|
private JPanel toolbarPane;//撤销重做 等工具栏 + 模板tab标签 + maybe have cpt 字体
|
||||||
|
|
||||||
|
private JComponent toolbarComponent;//cpt 字体 等工具栏
|
||||||
|
|
||||||
|
private JPanel eastPane;//=largeToolbar+eastCenterPane
|
||||||
|
private UILargeToolbar largeToolbar;//预览
|
||||||
|
|
||||||
|
private JPanel eastCenterPane;//=combineUp + templateTabPane
|
||||||
|
|
||||||
|
private UIToolbar combineUp;//撤销重做 等工具栏
|
||||||
|
|
||||||
|
private JPanel templateTabPane;//新建模板 + 模板tab标签
|
||||||
|
private NewTemplatePane newWorkBookPane;//新建模板button
|
||||||
|
|
||||||
|
|
||||||
|
public static CenterRegionContainerPane getInstance() { |
||||||
|
if (THIS == null) { |
||||||
|
synchronized (CenterRegionContainerPane.class) { |
||||||
|
if (THIS == null) { |
||||||
|
THIS = new CenterRegionContainerPane(); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
return THIS; |
||||||
|
} |
||||||
|
|
||||||
|
public CenterRegionContainerPane() { |
||||||
|
|
||||||
|
toolbarPane = new JPanel() { |
||||||
|
|
||||||
|
@Override |
||||||
|
public Dimension getPreferredSize() { |
||||||
|
|
||||||
|
Dimension dim = super.getPreferredSize(); |
||||||
|
// dim.height = TOOLBAR_HEIGHT;
|
||||||
|
return dim; |
||||||
|
} |
||||||
|
}; |
||||||
|
toolbarPane.setLayout(FRGUIPaneFactory.createBorderLayout()); |
||||||
|
eastPane = FRGUIPaneFactory.createBorderLayout_S_Pane(); |
||||||
|
eastPane.add(largeToolbar = getToolBarMenuDock().createLargeToolbar(), BorderLayout.WEST); |
||||||
|
eastCenterPane = FRGUIPaneFactory.createBorderLayout_S_Pane(); |
||||||
|
combineUpTooBar(); |
||||||
|
eastCenterPane.add(combineUp, BorderLayout.NORTH); |
||||||
|
templateTabPane = FRGUIPaneFactory.createBorderLayout_S_Pane(); |
||||||
|
templateTabPane.add(newWorkBookPane = getToolBarMenuDock().getNewTemplatePane(), BorderLayout.WEST); |
||||||
|
templateTabPane.add(MutilTempalteTabPane.getInstance(), BorderLayout.CENTER); |
||||||
|
eastCenterPane.add(templateTabPane, BorderLayout.CENTER); |
||||||
|
|
||||||
|
eastPane.add(eastCenterPane, BorderLayout.CENTER); |
||||||
|
toolbarPane.add(eastPane, BorderLayout.NORTH); |
||||||
|
toolbarPane.add(new UIMenuHighLight(), BorderLayout.SOUTH); |
||||||
|
|
||||||
|
this.setLayout(new BorderLayout()); |
||||||
|
this.add(centerTemplateCardPane = new DesktopCardPane(), BorderLayout.CENTER); |
||||||
|
this.add(toolbarPane, BorderLayout.NORTH); |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
private ToolBarMenuDock getToolBarMenuDock() { |
||||||
|
return DesignerContext.getDesignerFrame().getToolBarMenuDock(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 创建上工具栏 |
||||||
|
*/ |
||||||
|
private void combineUpTooBar() { |
||||||
|
combineUp = new UIToolbar(FlowLayout.LEFT); |
||||||
|
combineUp.setBorder(new MatteBorder(new Insets(0, LEFT_ALIGN_GAP, 1, 0), UIConstants.LINE_COLOR)); |
||||||
|
combineUp.setLayout(new FlowLayout(FlowLayout.LEFT, 5, 2)); |
||||||
|
setUpUpToolBar(null); |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* 重置上工具栏 |
||||||
|
*/ |
||||||
|
private void resetCombineUpTooBar(JComponent[] toolbar4Form, ToolBarMenuDockPlus plus) { |
||||||
|
combineUp.removeAll(); |
||||||
|
setUpUpToolBar(toolbar4Form); |
||||||
|
plus.insertToCombineUpToolbar(combineUp); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* 填充上工具栏的中的工具 |
||||||
|
* |
||||||
|
* @param toolbar4Form 目标组件 |
||||||
|
*/ |
||||||
|
private void setUpUpToolBar(@Nullable JComponent[] toolbar4Form) { |
||||||
|
UIButton[] fixButtons = getToolBarMenuDock().createUp(); |
||||||
|
for (UIButton fixButton : fixButtons) { |
||||||
|
combineUp.add(fixButton); |
||||||
|
} |
||||||
|
if (!DesignModeContext.isAuthorityEditing()) { |
||||||
|
combineUp.addSeparator(new Dimension(2, 16)); |
||||||
|
if (toolbar4Form != null) { |
||||||
|
for (JComponent jComponent : toolbar4Form) { |
||||||
|
combineUp.add(jComponent); |
||||||
|
} |
||||||
|
} |
||||||
|
//添加检测按钮
|
||||||
|
addCheckButton(); |
||||||
|
} |
||||||
|
//添加分享按钮
|
||||||
|
addShareButton(); |
||||||
|
//添加插件中的按钮
|
||||||
|
addExtraButtons(); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
private void addExtraButtons() { |
||||||
|
|
||||||
|
JTemplate<?, ?> jt = HistoryTemplateListCache.getInstance().getCurrentEditingTemplate(); |
||||||
|
if (jt == null) { |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
UIButton[] extraButtons = jt.createExtraButtons(); |
||||||
|
for (UIButton extraButton : extraButtons) { |
||||||
|
combineUp.add(extraButton); |
||||||
|
} |
||||||
|
if (extraButtons.length > 0) { |
||||||
|
combineUp.addSeparator(new Dimension(2, 16)); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private void addCheckButton() { |
||||||
|
JTemplate<?, ?> jt = HistoryTemplateListCache.getInstance().getCurrentEditingTemplate(); |
||||||
|
if (jt == null) { |
||||||
|
return; |
||||||
|
} |
||||||
|
combineUp.addSeparator(new Dimension(2, 16)); |
||||||
|
UIButton[] checkButtons = jt.createCheckButton(); |
||||||
|
for (UIButton checkButton : checkButtons) { |
||||||
|
combineUp.add(checkButton); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private void addShareButton() { |
||||||
|
|
||||||
|
JTemplate<?, ?> jt = HistoryTemplateListCache.getInstance().getCurrentEditingTemplate(); |
||||||
|
if (jt == null) { |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
combineUp.addSeparator(new Dimension(2, 16)); |
||||||
|
UIButton[] shareButtons = jt.createShareButton(); |
||||||
|
for (UIButton shareButton : shareButtons) { |
||||||
|
combineUp.add(shareButton); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 检查 |
||||||
|
* |
||||||
|
* @param flag 组件是否可见 |
||||||
|
* @param al 组件名称 |
||||||
|
*/ |
||||||
|
protected void checkCombineUp(boolean flag, ArrayList<String> al) { |
||||||
|
//Yvan: 检查当前是否为WORK_SHEET状态,因为只有WORK_SHEET中含有格式刷组件,此时是不需要进行checkComponentsByNames的
|
||||||
|
JTemplate<?, ?> jTemplate = HistoryTemplateListCache.getInstance().getCurrentEditingTemplate(); |
||||||
|
if (jTemplate != null) { |
||||||
|
// 第一个条件满足后还需要添加一重判断,判断是编辑报表块还是参数面板,编辑报表块时则直接return
|
||||||
|
if (jTemplate.getMenuState() == DesignState.WORK_SHEET && !jTemplate.isUpMode()) { |
||||||
|
return; |
||||||
|
} |
||||||
|
combineUp.checkComponentsByNames(flag, al); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* 重置相关的工具条. |
||||||
|
* |
||||||
|
* @param plus 工具条中相关信息 |
||||||
|
*/ |
||||||
|
protected void resetToolkitByPlus(ToolBarMenuDockPlus plus, ToolBarMenuDock ad) { |
||||||
|
|
||||||
|
resetCombineUpTooBar(ad.resetUpToolBar(plus), plus); |
||||||
|
|
||||||
|
if (toolbarComponent != null) { |
||||||
|
toolbarPane.remove(toolbarComponent); |
||||||
|
} |
||||||
|
|
||||||
|
// 颜色,字体那些按钮的工具栏
|
||||||
|
toolbarPane.add(toolbarComponent = ad.resetToolBar(toolbarComponent, plus), BorderLayout.CENTER); |
||||||
|
|
||||||
|
if (plus.hasToolBarPane()) { |
||||||
|
this.add(toolbarPane, BorderLayout.NORTH); |
||||||
|
} else { |
||||||
|
this.remove(toolbarPane); |
||||||
|
} |
||||||
|
|
||||||
|
resetByDesignMode(); |
||||||
|
} |
||||||
|
|
||||||
|
private void resetByDesignMode() { |
||||||
|
if (DesignModeContext.isDuchampMode()) { |
||||||
|
eastPane.remove(largeToolbar); |
||||||
|
eastCenterPane.remove(templateTabPane); |
||||||
|
centerTemplateCardPane.refresh(HistoryTemplateListCache.getInstance().getCurrentEditingTemplate()); |
||||||
|
} else { |
||||||
|
eastPane.add(largeToolbar, BorderLayout.WEST); |
||||||
|
eastCenterPane.add(templateTabPane, BorderLayout.CENTER); |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
JComponent getToolbarComponent() { |
||||||
|
|
||||||
|
return this.toolbarComponent; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 判断是否在权限编辑状态,若是在权限编辑状态,则需要有虚线框和关闭突变 |
||||||
|
*/ |
||||||
|
protected void needToAddAuhtorityPaint() { |
||||||
|
newWorkBookPane.setButtonGray(DesignModeContext.isAuthorityEditing()); |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
protected DesktopCardPane getCenterTemplateCardPane() { |
||||||
|
|
||||||
|
return centerTemplateCardPane; |
||||||
|
} |
||||||
|
|
||||||
|
protected void refreshUIToolBar() { |
||||||
|
if (toolbarComponent instanceof UIToolbar) { |
||||||
|
((UIToolbar ) toolbarComponent).refreshUIToolBar(); |
||||||
|
} |
||||||
|
combineUp.refreshUIToolBar(); |
||||||
|
getToolBarMenuDock().updateEnable(); |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,83 @@ |
|||||||
|
package com.fr.design.mainframe; |
||||||
|
|
||||||
|
import com.fr.design.file.TemplateTreePane; |
||||||
|
import com.fr.design.gui.itree.filetree.TemplateFileTree; |
||||||
|
import com.fr.stable.StringUtils; |
||||||
|
|
||||||
|
import javax.swing.tree.DefaultMutableTreeNode; |
||||||
|
import java.util.ArrayList; |
||||||
|
import java.util.Collections; |
||||||
|
import java.util.List; |
||||||
|
|
||||||
|
/** |
||||||
|
* @author shine |
||||||
|
* @version 10.0 |
||||||
|
* Created by shine on 2021/5/7 |
||||||
|
*/ |
||||||
|
public class JTemplateNameHelper { |
||||||
|
|
||||||
|
private static final int PREFIX_NUM = 3000; |
||||||
|
|
||||||
|
private static short currentIndex = 0;// 此变量用于多次新建模板时,让名字不重复
|
||||||
|
|
||||||
|
public static String newTemplateNameByIndex(String prefix) { |
||||||
|
// 用于获取左侧模板的文件名,如左侧已包含"WorkBook1.cpt, WorkBook12.cpt, WorkBook177.cpt"
|
||||||
|
// 那么新建的文件名将被命名为"WorkBook178.cpt",即取最大数+1
|
||||||
|
TemplateFileTree tt = TemplateTreePane.getInstance().getTemplateFileTree(); |
||||||
|
DefaultMutableTreeNode gen = (DefaultMutableTreeNode) tt.getModel().getRoot(); |
||||||
|
String[] str = new String[gen.getChildCount()]; |
||||||
|
|
||||||
|
List<Integer> reportNum = new ArrayList<>(); |
||||||
|
for (int j = 0; j < gen.getChildCount(); j++) { |
||||||
|
str[j] = gen.getChildAt(j).toString(); |
||||||
|
//返回文件名中的index(算法中没有再匹配文件后缀了,因为DefaultMutableTreeNode中已经匹配过了)
|
||||||
|
Integer index = getFileNameIndex(prefix, str[j]); |
||||||
|
if (index != null) { |
||||||
|
reportNum.add(index); |
||||||
|
} |
||||||
|
} |
||||||
|
Collections.sort(reportNum); |
||||||
|
int idx = reportNum.size() > 0 ? reportNum.get(reportNum.size() - 1) + 1 : 1; |
||||||
|
|
||||||
|
idx = idx + currentIndex; |
||||||
|
currentIndex++; |
||||||
|
return prefix + idx; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* @return java.lang.Integer WorkBook11.cpt则返回11,如果没有找到index返回null |
||||||
|
* @Description 返回文件名中的index |
||||||
|
* @param: prefix 前缀 |
||||||
|
* @param: fileName 文件名称全名 |
||||||
|
* @Author Henry.Wang |
||||||
|
* @Date 2021/4/9 11:13 |
||||||
|
**/ |
||||||
|
private static Integer getFileNameIndex(String prefix, String fileName) { |
||||||
|
char[] chars = new char[fileName.length()]; |
||||||
|
int i = 0; |
||||||
|
for (; i < fileName.length(); i++) { |
||||||
|
char c = fileName.charAt(i); |
||||||
|
//匹配前缀
|
||||||
|
if (i < prefix.length()) { |
||||||
|
if (c != prefix.charAt(i)) { |
||||||
|
return null; |
||||||
|
} |
||||||
|
} else { |
||||||
|
if (c == '.') { |
||||||
|
break; |
||||||
|
} else { |
||||||
|
//匹配0~9
|
||||||
|
if (c < 48 || c > 57) { |
||||||
|
return null; |
||||||
|
} |
||||||
|
chars[i - prefix.length()] = c; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
String s = new String(chars).substring(0, i - prefix.length()); |
||||||
|
if (StringUtils.isBlank(s)) { |
||||||
|
return null; |
||||||
|
} |
||||||
|
return Integer.valueOf(s); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,144 @@ |
|||||||
|
package com.fr.design.mainframe; |
||||||
|
|
||||||
|
import com.fr.design.DesignState; |
||||||
|
import com.fr.design.DesignerEnvManager; |
||||||
|
import com.fr.design.ExtraDesignClassManager; |
||||||
|
import com.fr.design.fun.TitlePlaceProcessor; |
||||||
|
import com.fr.design.gui.imenu.UIMenuHighLight; |
||||||
|
import com.fr.design.layout.FRGUIPaneFactory; |
||||||
|
import com.fr.design.mainframe.loghandler.LogMessageBar; |
||||||
|
import com.fr.design.mainframe.toolbar.ToolBarMenuDock; |
||||||
|
import com.fr.design.mainframe.toolbar.ToolBarMenuDockPlus; |
||||||
|
import com.fr.design.menu.MenuManager; |
||||||
|
import com.fr.design.os.impl.SupportOSImpl; |
||||||
|
import com.fr.general.GeneralContext; |
||||||
|
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.stable.os.support.OSBasedAction; |
||||||
|
import com.fr.stable.os.support.OSSupportCenter; |
||||||
|
|
||||||
|
import javax.swing.JMenuBar; |
||||||
|
import javax.swing.JPanel; |
||||||
|
import javax.swing.SwingUtilities; |
||||||
|
import java.awt.BorderLayout; |
||||||
|
import java.awt.Component; |
||||||
|
import java.awt.FlowLayout; |
||||||
|
|
||||||
|
/** |
||||||
|
* @author shine |
||||||
|
* @version 10.0 |
||||||
|
* Created by shine on 2021/4/6 |
||||||
|
*/ |
||||||
|
public class NorthRegionContainerPane extends JPanel { |
||||||
|
|
||||||
|
private static volatile NorthRegionContainerPane THIS; |
||||||
|
|
||||||
|
private JMenuBar menuBar; |
||||||
|
|
||||||
|
public static NorthRegionContainerPane getInstance() { |
||||||
|
if (THIS == null) { |
||||||
|
synchronized (NorthRegionContainerPane.class) { |
||||||
|
if (THIS == null) { |
||||||
|
THIS = new NorthRegionContainerPane(); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
return THIS; |
||||||
|
} |
||||||
|
|
||||||
|
public NorthRegionContainerPane() { |
||||||
|
|
||||||
|
ToolBarMenuDock ad = DesignerContext.getDesignerFrame().getToolBarMenuDock(); |
||||||
|
|
||||||
|
this.setLayout(new BorderLayout()); |
||||||
|
this.add(new UIMenuHighLight(), BorderLayout.SOUTH); |
||||||
|
this.add(initNorthEastPane(ad), BorderLayout.EAST); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* @param ad 菜单栏 |
||||||
|
* @return panel |
||||||
|
*/ |
||||||
|
protected JPanel initNorthEastPane(final ToolBarMenuDock ad) { |
||||||
|
//hugh: private修改为protected方便oem的时候修改右上的组件构成
|
||||||
|
//顶部日志+登陆按钮
|
||||||
|
final JPanel northEastPane = FRGUIPaneFactory.createBorderLayout_S_Pane(); |
||||||
|
//优先级为-1,保证最后全面刷新一次
|
||||||
|
GeneralContext.listenPluginRunningChanged(new PluginEventListener(-1) { |
||||||
|
|
||||||
|
@Override |
||||||
|
public void on(PluginEvent event) { |
||||||
|
|
||||||
|
refreshNorthEastPane(northEastPane, ad); |
||||||
|
SwingUtilities.invokeLater(new Runnable() { |
||||||
|
@Override |
||||||
|
public void run() { |
||||||
|
if (DesignerContext.getDesignerFrame() == null) { |
||||||
|
return; |
||||||
|
} |
||||||
|
DesignerContext.getDesignerFrame().refresh(); |
||||||
|
DesignerContext.getDesignerFrame().repaint(); |
||||||
|
} |
||||||
|
}); |
||||||
|
} |
||||||
|
}, new PluginFilter() { |
||||||
|
|
||||||
|
@Override |
||||||
|
public boolean accept(PluginContext context) { |
||||||
|
|
||||||
|
return context.contain(PluginModule.ExtraDesign); |
||||||
|
} |
||||||
|
}); |
||||||
|
refreshNorthEastPane(northEastPane, ad); |
||||||
|
return northEastPane; |
||||||
|
} |
||||||
|
|
||||||
|
private void refreshNorthEastPane(final JPanel northEastPane, final ToolBarMenuDock ad) { |
||||||
|
|
||||||
|
northEastPane.removeAll(); |
||||||
|
northEastPane.setLayout(new FlowLayout(FlowLayout.RIGHT, 0, 0)); |
||||||
|
northEastPane.add(LogMessageBar.getInstance()); |
||||||
|
TitlePlaceProcessor processor = ExtraDesignClassManager.getInstance().getSingle(TitlePlaceProcessor.MARK_STRING); |
||||||
|
if (processor != null) { |
||||||
|
final Component[] bbsLoginPane = {null}; |
||||||
|
OSSupportCenter.buildAction(new OSBasedAction() { |
||||||
|
@Override |
||||||
|
public void execute(Object... objects) { |
||||||
|
bbsLoginPane[0] = ad.createBBSLoginPane(); |
||||||
|
} |
||||||
|
}, SupportOSImpl.USERINFOPANE); |
||||||
|
processor.hold(northEastPane, LogMessageBar.getInstance(), bbsLoginPane[0]); |
||||||
|
} |
||||||
|
northEastPane.add(ad.createAlphaFinePane()); |
||||||
|
if (!DesignerEnvManager.getEnvManager().getAlphaFineConfigManager().isEnabled()) { |
||||||
|
ad.createAlphaFinePane().setVisible(false); |
||||||
|
} |
||||||
|
northEastPane.add(ad.createNotificationCenterPane()); |
||||||
|
|
||||||
|
OSSupportCenter.buildAction(new OSBasedAction() { |
||||||
|
@Override |
||||||
|
public void execute(Object... objects) { |
||||||
|
northEastPane.add(ad.createBBSLoginPane()); |
||||||
|
} |
||||||
|
}, SupportOSImpl.USERINFOPANE); |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 重置相关的工具条. |
||||||
|
* |
||||||
|
* @param plus 工具条中相关信息 |
||||||
|
*/ |
||||||
|
void resetToolkitByPlus(ToolBarMenuDockPlus plus, ToolBarMenuDock ad) { |
||||||
|
DesignState designState = new DesignState(plus); |
||||||
|
MenuManager.getInstance().setMenus4Designer(designState); |
||||||
|
if (menuBar == null) { |
||||||
|
this.add(menuBar = ad.createJMenuBar(plus), BorderLayout.CENTER); |
||||||
|
} else { |
||||||
|
ad.resetJMenuBar(menuBar, plus); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
After Width: | Height: | Size: 401 B |
After Width: | Height: | Size: 131 B |
After Width: | Height: | Size: 133 B |
After Width: | Height: | Size: 133 B |
After Width: | Height: | Size: 271 B |
After Width: | Height: | Size: 132 B |
After Width: | Height: | Size: 130 B |
After Width: | Height: | Size: 126 B |
After Width: | Height: | Size: 137 B |
After Width: | Height: | Size: 147 B |
After Width: | Height: | Size: 249 B |
@ -0,0 +1,23 @@ |
|||||||
|
package com.fr.design.mainframe; |
||||||
|
|
||||||
|
import junit.framework.TestCase; |
||||||
|
|
||||||
|
/** |
||||||
|
* @author shine |
||||||
|
* @version 10.0 |
||||||
|
* Created by shine on 2021/5/8 |
||||||
|
*/ |
||||||
|
public class JTemplateNameHelperTest extends TestCase { |
||||||
|
|
||||||
|
public void testNewTemplateNameByIndex() { |
||||||
|
|
||||||
|
String name = JTemplateNameHelper.newTemplateNameByIndex("TEST"); |
||||||
|
|
||||||
|
assertEquals("TEST1", name); |
||||||
|
|
||||||
|
String name1 = JTemplateNameHelper.newTemplateNameByIndex("TEST"); |
||||||
|
|
||||||
|
assertEquals("TEST2", name1); |
||||||
|
|
||||||
|
} |
||||||
|
} |
@ -0,0 +1,38 @@ |
|||||||
|
package com.fr.design.chart.fun.impl; |
||||||
|
|
||||||
|
import com.fr.chart.charttypes.ChartTypeManager; |
||||||
|
import com.fr.chartx.attr.ChartProvider; |
||||||
|
import com.fr.design.ChartTypeInterfaceManager; |
||||||
|
import com.fr.design.mainframe.chart.gui.type.AbstractChartTypePane; |
||||||
|
|
||||||
|
/** |
||||||
|
* @author shine |
||||||
|
* @version 10.0 |
||||||
|
* Created by shine on 2020/7/8 |
||||||
|
*/ |
||||||
|
public class DefaultChartTypePane<T extends ChartProvider> extends AbstractChartTypePane<T> { |
||||||
|
|
||||||
|
@Override |
||||||
|
protected String[] getTypeIconPath() { |
||||||
|
return ChartTypeInterfaceManager.getInstance().getDemoImagePath(this.getPlotID()); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
protected String[] getTypeTipName() { |
||||||
|
return ChartTypeInterfaceManager.getInstance().getSubName(this.getPlotID()); |
||||||
|
} |
||||||
|
|
||||||
|
public ChartProvider getDefaultChart() { |
||||||
|
return ChartTypeManager.getInstance().getChartTypes(this.getPlotID())[0]; |
||||||
|
} |
||||||
|
|
||||||
|
public String title4PopupWindow() { |
||||||
|
return ChartTypeInterfaceManager.getInstance().getName(this.getPlotID()); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void populateBean(T ob) { |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
} |
@ -0,0 +1,15 @@ |
|||||||
|
package com.fr.design.chart.fun.impl; |
||||||
|
|
||||||
|
import com.fr.design.ChartTypeInterfaceManager; |
||||||
|
|
||||||
|
/** |
||||||
|
* @author shine |
||||||
|
* @version 10.0 |
||||||
|
* Created by shine on 2021/6/25 |
||||||
|
*/ |
||||||
|
public class InvisibleChartTypePane extends DefaultChartTypePane { |
||||||
|
@Override |
||||||
|
public String title4PopupWindow() { |
||||||
|
return ChartTypeInterfaceManager.TYPE_PANE_DEFAULT_TITLE; |
||||||
|
} |
||||||
|
} |
@ -1,112 +0,0 @@ |
|||||||
package com.fr.design.mainframe; |
|
||||||
|
|
||||||
import com.fr.chart.base.MapSvgAttr; |
|
||||||
import com.fr.chart.base.MapSvgXMLHelper; |
|
||||||
import com.fr.chart.chartglyph.MapShapeValue; |
|
||||||
import com.fr.design.beans.BasicBeanPane; |
|
||||||
import com.fr.design.chart.series.PlotSeries.AbstrctMapAttrEditPane; |
|
||||||
import com.fr.design.chart.series.PlotSeries.MapCustomPane; |
|
||||||
import com.fr.design.chart.series.PlotSeries.MapDefiAreaNamePane; |
|
||||||
import com.fr.design.gui.frpane.UITabbedPane; |
|
||||||
|
|
||||||
import com.fr.stable.CoreConstants; |
|
||||||
import com.fr.stable.StringUtils; |
|
||||||
|
|
||||||
import javax.swing.event.ChangeEvent; |
|
||||||
import javax.swing.event.ChangeListener; |
|
||||||
import java.awt.*; |
|
||||||
|
|
||||||
/** |
|
||||||
* Created by IntelliJ IDEA. |
|
||||||
* Author : daisy |
|
||||||
* Version: 7.1.1 |
|
||||||
* Date: 14/12/2 |
|
||||||
* Time: 下午7:17 |
|
||||||
*/ |
|
||||||
public class MapEditPane extends BasicBeanPane<MapSvgAttr>{ |
|
||||||
|
|
||||||
private UITabbedPane tabbedPane; |
|
||||||
private MapCustomPane areaPane ; |
|
||||||
// private MapCustomPane pointPane;
|
|
||||||
private MapDefiAreaNamePane namedPane; |
|
||||||
private String currentMapName; |
|
||||||
private AbstrctMapAttrEditPane editingPane; |
|
||||||
|
|
||||||
private ChangeListener tabbedChangeListener = new ChangeListener() { |
|
||||||
@Override |
|
||||||
public void stateChanged(ChangeEvent e) { |
|
||||||
switch ( tabbedPane.getSelectedIndex()){ |
|
||||||
case 1: |
|
||||||
namedPane.populateMapAttr(editingPane.updateCurrentAttr()); |
|
||||||
editingPane = namedPane; |
|
||||||
break; |
|
||||||
default: |
|
||||||
areaPane.populateMapAttr(editingPane.updateCurrentAttr()); |
|
||||||
editingPane = areaPane; |
|
||||||
} |
|
||||||
} |
|
||||||
} ; |
|
||||||
|
|
||||||
public MapEditPane(){ |
|
||||||
initTabbedPane(); |
|
||||||
this.setLayout(new BorderLayout()); |
|
||||||
this.add(tabbedPane,BorderLayout.CENTER); |
|
||||||
} |
|
||||||
|
|
||||||
private void initTabbedPane(){ |
|
||||||
tabbedPane = new UITabbedPane(); |
|
||||||
areaPane = new MapCustomPane(false); |
|
||||||
// pointPane = new MapCustomPane(false);
|
|
||||||
namedPane= new MapDefiAreaNamePane(false); |
|
||||||
areaPane.setImageSelectType(MapShapeValue.AREA); |
|
||||||
// pointPane.setImageSelectType(MapShapeValue.POINT);
|
|
||||||
tabbedPane.add(com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Chart_Map_Image_Area"),areaPane); |
|
||||||
// tabbedPane.add(com.fr.design.i18n.Toolkit.i18nText("FR-Chart-Map_ImagePoint"),pointPane);
|
|
||||||
tabbedPane.add(com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Chart_Map_Corresponding_Fields"),namedPane); |
|
||||||
editingPane = areaPane; |
|
||||||
} |
|
||||||
|
|
||||||
@Override |
|
||||||
protected String title4PopupWindow() { |
|
||||||
return null; |
|
||||||
} |
|
||||||
|
|
||||||
@Override |
|
||||||
public void populateBean(MapSvgAttr ob) { |
|
||||||
if(!StringUtils.isEmpty(ob.getName()) && !MapSvgXMLHelper.getInstance().containsMapName(ob.getName())){ |
|
||||||
MapSvgAttr mapSvgAttr = new MapSvgAttr(); |
|
||||||
mapSvgAttr.setFilePath(MapSvgXMLHelper.customMapPath()+ CoreConstants.SEPARATOR+ob.getName()+".svg"); |
|
||||||
MapSvgXMLHelper.getInstance().addNewSvgMaps(ob.getName(), mapSvgAttr); |
|
||||||
} |
|
||||||
|
|
||||||
currentMapName = ob.getName(); |
|
||||||
if(editingPane == null){ |
|
||||||
editingPane = areaPane; |
|
||||||
} |
|
||||||
editingPane.populateMapAttr(ob); |
|
||||||
tabbedPane.addChangeListener(tabbedChangeListener); |
|
||||||
} |
|
||||||
|
|
||||||
public String getCurrentMapName(){ |
|
||||||
return currentMapName; |
|
||||||
} |
|
||||||
|
|
||||||
public void setCurrentMapName(String currentMapName){ |
|
||||||
this.currentMapName = currentMapName; |
|
||||||
|
|
||||||
} |
|
||||||
|
|
||||||
@Override |
|
||||||
public MapSvgAttr updateBean() { |
|
||||||
MapSvgAttr currentAttr = editingPane.updateCurrentAttr(); |
|
||||||
currentMapName =currentAttr != null ? currentAttr.getName() : currentMapName; |
|
||||||
MapSvgAttr attr = MapSvgXMLHelper.getInstance().getMapAttr(currentMapName); |
|
||||||
if(attr != null ){ |
|
||||||
MapSvgXMLHelper.getInstance().removeNewMapAttr(currentMapName); |
|
||||||
MapSvgXMLHelper.getInstance().pushMapAttr(currentMapName,currentAttr); |
|
||||||
return currentAttr; |
|
||||||
}else{ |
|
||||||
return MapSvgXMLHelper.getInstance().getNewMapAttr(currentMapName); |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
@ -0,0 +1,26 @@ |
|||||||
|
package com.fr.design.mainframe.chart.mode; |
||||||
|
|
||||||
|
import com.fr.common.annotations.Open; |
||||||
|
|
||||||
|
/** |
||||||
|
* @author shine |
||||||
|
* @version 10.0 |
||||||
|
* Created by shine on 2021/6/4 |
||||||
|
*/ |
||||||
|
@Open |
||||||
|
public class ChartEditContext { |
||||||
|
|
||||||
|
private static ChartEditMode current = ChartEditMode.NORMAL; |
||||||
|
|
||||||
|
public static void switchTo(ChartEditMode mode) { |
||||||
|
current = mode; |
||||||
|
} |
||||||
|
|
||||||
|
public static boolean duchampMode() { |
||||||
|
return current == ChartEditMode.DUCHAMP; |
||||||
|
} |
||||||
|
|
||||||
|
public static boolean normalMode() { |
||||||
|
return current == ChartEditMode.NORMAL; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,12 @@ |
|||||||
|
package com.fr.design.mainframe.chart.mode; |
||||||
|
|
||||||
|
/** |
||||||
|
* @author shine |
||||||
|
* @version 10.0 |
||||||
|
* Created by shine on 2021/6/4 |
||||||
|
*/ |
||||||
|
//todo:refactor 弹出框图表没有单元格数据源,就不用一层层传下去了
|
||||||
|
public enum ChartEditMode { |
||||||
|
NORMAL, |
||||||
|
DUCHAMP |
||||||
|
} |
@ -0,0 +1,70 @@ |
|||||||
|
package com.fr.design.mainframe; |
||||||
|
|
||||||
|
import java.awt.Point; |
||||||
|
|
||||||
|
public abstract class AbstractFormParallelLine { |
||||||
|
protected int parallelValue; |
||||||
|
protected int startPosition; |
||||||
|
protected int endPosition; |
||||||
|
|
||||||
|
public AbstractFormParallelLine(int parallelValue, int startPosition, int endPosition) { |
||||||
|
this.parallelValue = parallelValue; |
||||||
|
this.startPosition = startPosition; |
||||||
|
this.endPosition = endPosition; |
||||||
|
} |
||||||
|
|
||||||
|
public int getCenterPosition() { |
||||||
|
return (startPosition + endPosition) / 2; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 获取当前直线的中垂线起点位置 |
||||||
|
* @return 中垂线起点位置 |
||||||
|
*/ |
||||||
|
abstract public Point getStartPointOnVerticalCenterLine(); |
||||||
|
|
||||||
|
/** |
||||||
|
* 获取当前直线的中垂线的重点位置,重点位置即为重垂线与另一条平行线相交的点 |
||||||
|
* @param parallelValue 平行线 |
||||||
|
* @return 中垂线重点位置 |
||||||
|
*/ |
||||||
|
abstract public Point getEndPointOnVerticalCenterLine(int parallelValue); |
||||||
|
|
||||||
|
public boolean isVerticalCenterLineBeforeTheParallelLine(AbstractFormParallelLine parallelLine) { |
||||||
|
return this.getCenterPosition() < parallelLine.getStartPosition(); |
||||||
|
} |
||||||
|
|
||||||
|
public boolean isVerticalCenterLineBehindTheParallelLine(AbstractFormParallelLine parallelLine) { |
||||||
|
return this.getCenterPosition() > parallelLine.getEndPosition(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 传一个平行线,当 当前直线和平行线中心点位置无平行相交的部分的时候,需要绘制一条延长线,一直延长到平行线边界位置 |
||||||
|
* @param parallelLine 平行线 |
||||||
|
* @return 延长线的起点位置 |
||||||
|
*/ |
||||||
|
abstract public Point getExtendedLineStartPoint(AbstractFormParallelLine parallelLine); |
||||||
|
|
||||||
|
/** |
||||||
|
* 传一个平行线,当 当前直线和平行线中心点位置无平行相交的部分的时候,需要绘制一条延长线,一直延长到平行线边界位置 |
||||||
|
* @param parallelLine 平行线 |
||||||
|
* @return 延长线的重点位置 |
||||||
|
*/ |
||||||
|
abstract public Point getExtendedLineEndPoint(AbstractFormParallelLine parallelLine); |
||||||
|
|
||||||
|
public int getDistanceWithLine(AbstractFormParallelLine parallelLine) { |
||||||
|
return Math.abs(this.getParallelValue() - parallelLine.getParallelValue()); |
||||||
|
} |
||||||
|
|
||||||
|
public int getParallelValue() { |
||||||
|
return parallelValue; |
||||||
|
} |
||||||
|
|
||||||
|
public int getStartPosition() { |
||||||
|
return startPosition; |
||||||
|
} |
||||||
|
|
||||||
|
public int getEndPosition() { |
||||||
|
return endPosition; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,45 @@ |
|||||||
|
package com.fr.design.mainframe; |
||||||
|
|
||||||
|
import java.awt.Point; |
||||||
|
|
||||||
|
public class FormHorizontalParallelLine extends AbstractFormParallelLine { |
||||||
|
public FormHorizontalParallelLine(int parallelValue, int startPosition, int endPosition) { |
||||||
|
super(parallelValue, startPosition, endPosition); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public Point getStartPointOnVerticalCenterLine() { |
||||||
|
Point point = new Point(); |
||||||
|
point.setLocation(getCenterPosition(), parallelValue); |
||||||
|
return point; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public Point getEndPointOnVerticalCenterLine(int parallelValue) { |
||||||
|
Point point = new Point(); |
||||||
|
point.setLocation(getCenterPosition(), parallelValue); |
||||||
|
return point; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public Point getExtendedLineStartPoint(AbstractFormParallelLine parallelLine) { |
||||||
|
Point point = new Point(); |
||||||
|
if (parallelLine.isVerticalCenterLineBeforeTheParallelLine(this)) { |
||||||
|
point.setLocation(getStartPosition(), getParallelValue()); |
||||||
|
} else if (parallelLine.isVerticalCenterLineBehindTheParallelLine(this)) { |
||||||
|
point.setLocation(getEndPosition(), getParallelValue()); |
||||||
|
} |
||||||
|
return point; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public Point getExtendedLineEndPoint(AbstractFormParallelLine parallelLine) { |
||||||
|
Point point = new Point(); |
||||||
|
if (parallelLine.isVerticalCenterLineBeforeTheParallelLine(this)) { |
||||||
|
point.setLocation(parallelLine.getStartPosition(), getParallelValue()); |
||||||
|
} else if (parallelLine.isVerticalCenterLineBehindTheParallelLine(this)) { |
||||||
|
point.setLocation(parallelLine.getEndPosition(), getParallelValue()); |
||||||
|
} |
||||||
|
return point; |
||||||
|
} |
||||||
|
} |