diff --git a/designer-base/src/main/java/com/fr/design/base/clipboard/ClipboardHelper.java b/designer-base/src/main/java/com/fr/design/base/clipboard/ClipboardHelper.java new file mode 100644 index 000000000..870af0be5 --- /dev/null +++ b/designer-base/src/main/java/com/fr/design/base/clipboard/ClipboardHelper.java @@ -0,0 +1,24 @@ +package com.fr.design.base.clipboard; + +import java.util.List; + +public class ClipboardHelper { + public static String formatExcelString(List> table) { + StringBuffer stringBuffer = new StringBuffer(); + + for (int row = 0; row < table.size(); row++) { + List 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(); + } +} diff --git a/designer-base/src/main/java/com/fr/design/data/datapane/preview/CopyableJTable.java b/designer-base/src/main/java/com/fr/design/data/datapane/preview/CopyableJTable.java new file mode 100644 index 000000000..38b5a08e4 --- /dev/null +++ b/designer-base/src/main/java/com/fr/design/data/datapane/preview/CopyableJTable.java @@ -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 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> 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; + } + +} diff --git a/designer-base/src/main/java/com/fr/design/data/datapane/preview/PreviewTablePane.java b/designer-base/src/main/java/com/fr/design/data/datapane/preview/PreviewTablePane.java index e95a5999a..409ae7f7f 100644 --- a/designer-base/src/main/java/com/fr/design/data/datapane/preview/PreviewTablePane.java +++ b/designer-base/src/main/java/com/fr/design/data/datapane/preview/PreviewTablePane.java @@ -26,7 +26,6 @@ import com.fr.design.i18n.Toolkit; import com.fr.design.layout.FRGUIPaneFactory; import com.fr.design.mainframe.DesignerContext; import com.fr.design.ui.util.UIUtil; -import com.fr.design.utils.gui.GUICoreUtils; import com.fr.function.TIME; import com.fr.general.FRFont; import com.fr.log.FineLoggerFactory; @@ -162,7 +161,7 @@ public class PreviewTablePane extends BasicPane { } }); - preveiwTable = new SortableJTable(new TableSorter()); + preveiwTable = new CopyableJTable(new TableSorter()); preveiwTable.setRowSelectionAllowed(false); preveiwTable.setAutoResizeMode(JTable.AUTO_RESIZE_OFF); diff --git a/designer-realize/src/main/java/com/fr/design/cell/clipboard/CellElementsClip.java b/designer-realize/src/main/java/com/fr/design/cell/clipboard/CellElementsClip.java index 2767b1a8e..8bc66e172 100644 --- a/designer-realize/src/main/java/com/fr/design/cell/clipboard/CellElementsClip.java +++ b/designer-realize/src/main/java/com/fr/design/cell/clipboard/CellElementsClip.java @@ -3,6 +3,7 @@ */ package com.fr.design.cell.clipboard; +import com.fr.design.base.clipboard.ClipboardHelper; import com.fr.grid.selection.CellSelection; import com.fr.log.FineLoggerFactory; import com.fr.report.cell.CellElement; @@ -12,8 +13,10 @@ import com.fr.report.elementcase.TemplateElementCase; import com.fr.stable.StringUtils; import com.fr.stable.unit.FU; +import java.util.ArrayList; import java.util.Arrays; import java.util.Iterator; +import java.util.List; /** * The clip of CellElement. @@ -21,16 +24,16 @@ import java.util.Iterator; public class CellElementsClip implements Cloneable, java.io.Serializable { private int columnSpan = 0; private int rowSpan = 0; - private FU[] columnWidth; - private FU[] rowHeight; + private FU[] columnWidth; + private FU[] rowHeight; private TemplateCellElement[] clips; - public CellElementsClip(int columnSpan, int rowSpan, FU[] columnWidth , FU[] rowHeight, TemplateCellElement[] clips) { - this.columnSpan = columnSpan; - this.rowSpan = rowSpan; - this.columnWidth = columnWidth ; - this.rowHeight = rowHeight; - this.clips = clips; + public CellElementsClip(int columnSpan, int rowSpan, FU[] columnWidth, FU[] rowHeight, TemplateCellElement[] clips) { + this.columnSpan = columnSpan; + this.rowSpan = rowSpan; + this.columnWidth = columnWidth; + this.rowHeight = rowHeight; + this.clips = clips; } public CellElementsClip(int columnSpan, int rowSpan, TemplateCellElement[] clips) { @@ -39,122 +42,116 @@ public class CellElementsClip implements Cloneable, java.io.Serializable { this.clips = clips; } - public int getColumnSpan() { - return columnSpan; - } + public int getColumnSpan() { + return columnSpan; + } - public void setColumnSpan(int columnSpan) { - this.columnSpan = columnSpan; - } + public void setColumnSpan(int columnSpan) { + this.columnSpan = columnSpan; + } - public int getRowSpan() { - return rowSpan; - } + public int getRowSpan() { + return rowSpan; + } - public void setRowSpan(int rowSpan) { - this.rowSpan = rowSpan; - } + public void setRowSpan(int rowSpan) { + this.rowSpan = rowSpan; + } - public FU[] getColumnWidth() { - return columnWidth; - } + public FU[] getColumnWidth() { + return columnWidth; + } - public void setColumnWidth(FU[] columnWidth) { - this.columnWidth = columnWidth; - } + public void setColumnWidth(FU[] columnWidth) { + this.columnWidth = columnWidth; + } - public FU[] getRowHeight() { - return rowHeight; - } + public FU[] getRowHeight() { + return rowHeight; + } - public void setRowHeight(FU[] rowHeight) { - this.rowHeight = rowHeight; - } + public void setRowHeight(FU[] rowHeight) { + this.rowHeight = rowHeight; + } - public TemplateCellElement[] getClips() { - return clips; - } + public TemplateCellElement[] getClips() { + return clips; + } - public void setClips(TemplateCellElement[] clips) { - this.clips = clips; - } + public void setClips(TemplateCellElement[] clips) { + this.clips = clips; + } - public String compateExcelPaste() { - Arrays.sort(this.clips, CellElementComparator.getRowFirstComparator()); + public String compateExcelPaste() { + Arrays.sort(this.clips, CellElementComparator.getRowFirstComparator()); - // 排序 - StringBuffer sbuf = new StringBuffer(); + // 排序 - int currentRow = -1; - for (int i = 0; i < clips.length; i++) { - CellElement cellElement = clips[i]; - if (currentRow == -1) {// 初始化当前行. - currentRow = cellElement.getRow(); - } + List> table = new ArrayList<>(); + int startRow = -1; + int currentRow = -1; + for (int i = 0; i < clips.length; i++) { + CellElement cellElement = clips[i]; + if (currentRow == -1) {// 初始化当前行. + currentRow = cellElement.getRow(); + startRow = currentRow; + table.add(new ArrayList<>()); + } - if (currentRow < cellElement.getRow()) { - for (int r = currentRow; r < cellElement.getRow(); r++) { - sbuf.append('\n'); - } - currentRow = cellElement.getRow(); - } + while (currentRow < cellElement.getRow()) { + table.add(new ArrayList<>()); + currentRow++; + } - // 添加分隔符号. - if (sbuf.length() > 0 && sbuf.charAt(sbuf.length() - 1) != '\n') { - sbuf.append('\t'); - } - //REPORT-5134:会复制出null - if (cellElement.getValue() == null) { - sbuf.append(StringUtils.EMPTY); - } else { - sbuf.append(cellElement.getValue()); - } - } + Object cellValue = cellElement.getValue() == null ? StringUtils.EMPTY : cellElement.getValue(); + table.get(currentRow - startRow).add(cellValue); + } - return sbuf.toString(); + return ClipboardHelper.formatExcelString(table); } + public CellSelection pasteAt(TemplateElementCase ec, int column, int row) { - Iterator cells = ec.intersect(column, row, columnSpan, rowSpan); - while (cells.hasNext()) { - TemplateCellElement cellElement = (TemplateCellElement)cells.next(); - ec.removeCellElement(cellElement); - } - for (int i = 0; i < clips.length; i++) { - TemplateCellElement cellElement; - try { - cellElement = (TemplateCellElement) clips[i].clone(); - } catch (CloneNotSupportedException e) { + Iterator cells = ec.intersect(column, row, columnSpan, rowSpan); + while (cells.hasNext()) { + TemplateCellElement cellElement = (TemplateCellElement) cells.next(); + ec.removeCellElement(cellElement); + } + for (int i = 0; i < clips.length; i++) { + TemplateCellElement cellElement; + try { + cellElement = (TemplateCellElement) clips[i].clone(); + } catch (CloneNotSupportedException e) { FineLoggerFactory.getLogger().error(e.getMessage(), e); - return null; - } - - // peter:因为前面已经将这个位置的元素删除了,所以不需要override了. - ec.addCellElement((TemplateCellElement) cellElement.deriveCellElement( - column + cellElement.getColumn(), row + cellElement.getRow() - ), false); - } - //设置单元格的宽高 - if(this.columnWidth != null && this.rowHeight != null){ + return null; + } + + // peter:因为前面已经将这个位置的元素删除了,所以不需要override了. + ec.addCellElement((TemplateCellElement) cellElement.deriveCellElement( + column + cellElement.getColumn(), row + cellElement.getRow() + ), false); + } + //设置单元格的宽高 + if (this.columnWidth != null && this.rowHeight != null) { pasteWidthAndHeight(ec, column, row, columnSpan, rowSpan); } - return new CellSelection(column, row, columnSpan, rowSpan); + return new CellSelection(column, row, columnSpan, rowSpan); } - public void pasteWidthAndHeight(TemplateElementCase ec, int column, int row, int columnSpan, int rowSpan){ - for(int i = 0; i < columnSpan; i++){ - ec.setColumnWidth(column + i, columnWidth[i]); - } - for(int j = 0; j < rowSpan; j++){ - ec.setRowHeight(row + j, rowHeight[j]); - } - } + public void pasteWidthAndHeight(TemplateElementCase ec, int column, int row, int columnSpan, int rowSpan) { + for (int i = 0; i < columnSpan; i++) { + ec.setColumnWidth(column + i, columnWidth[i]); + } + for (int j = 0; j < rowSpan; j++) { + ec.setRowHeight(row + j, rowHeight[j]); + } + } public void pasteAtRegion(TemplateElementCase ec, - int startColumn, int startRow, - int column, int row, - int columnSpan, int rowSpan) { + int startColumn, int startRow, + int column, int row, + int columnSpan, int rowSpan) { for (int i = 0; i < clips.length; i++) { TemplateCellElement cellElement = clips[i]; @@ -173,14 +170,14 @@ public class CellElementsClip implements Cloneable, java.io.Serializable { * Clone. */ @Override - public Object clone() throws CloneNotSupportedException { + public Object clone() throws CloneNotSupportedException { CellElementsClip cloned = (CellElementsClip) super.clone(); if (this.clips != null) { - cloned.clips = new TemplateCellElement[this.clips.length]; - for (int i = 0; i < this.clips.length; i++) { - cloned.clips[i] = (TemplateCellElement)this.clips[i].clone(); - } + cloned.clips = new TemplateCellElement[this.clips.length]; + for (int i = 0; i < this.clips.length; i++) { + cloned.clips[i] = (TemplateCellElement) this.clips[i].clone(); + } } return cloned;