package org.insightech.er.common.widgets; import java.awt.Color; import java.awt.Component; import java.awt.Dimension; import java.awt.EventQueue; import java.awt.Font; import java.awt.Point; import java.awt.Toolkit; import java.awt.datatransfer.Clipboard; import java.awt.datatransfer.ClipboardOwner; import java.awt.datatransfer.DataFlavor; import java.awt.datatransfer.StringSelection; import java.awt.datatransfer.Transferable; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.KeyAdapter; import java.awt.event.KeyEvent; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.awt.event.MouseMotionAdapter; import java.io.BufferedReader; import java.io.IOException; import java.io.StringReader; import java.lang.reflect.InvocationTargetException; import java.net.URL; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Scanner; import java.util.Vector; import javax.swing.DefaultListCellRenderer; import javax.swing.DefaultListModel; import javax.swing.ImageIcon; import javax.swing.JLabel; import javax.swing.JList; import javax.swing.JMenuItem; import javax.swing.JPopupMenu; import javax.swing.JScrollPane; import javax.swing.JTable; import javax.swing.ListCellRenderer; import javax.swing.SwingUtilities; import javax.swing.UIManager; import javax.swing.event.ChangeEvent; import javax.swing.event.ListSelectionEvent; import javax.swing.event.ListSelectionListener; import javax.swing.table.DefaultTableModel; import javax.swing.table.JTableHeader; import javax.swing.table.TableCellEditor; import javax.swing.table.TableCellRenderer; import javax.swing.table.TableColumn; import javax.swing.table.TableColumnModel; import org.eclipse.swt.graphics.FontData; import org.eclipse.swt.widgets.Display; import org.insightech.er.Activator; import org.insightech.er.ResourceString; import org.insightech.er.common.widgets.table.CellEditWorker; import org.insightech.er.common.widgets.table.CustomCellEditor; import org.insightech.er.common.widgets.table.HeaderClickListener; import org.insightech.er.common.widgets.table.PanelCellEditor; import org.insightech.er.util.Format; public class RowHeaderTable extends JScrollPane implements ClipboardOwner { private static final long serialVersionUID = 1L; private DefaultListModel listModel; private DefaultTableModel tableModel; private JTable table; private MultiLineHeaderRenderer headerRenderer; private MultiLineHeaderRenderer selectedHeaderRenderer; private CellEditWorker cellEditWorker; private Map<Integer, PanelCellEditor> cellEditorMap = new HashMap<Integer, PanelCellEditor>(); private boolean clipbordOn = true; private HeaderClickListener headerClickListener; private int mouseOverColumn = -1; private boolean editable; private Color MODIFIED_COLOR = new Color(0xc7, 0xff, 0xb7); public RowHeaderTable(int width, int height, final int rowHeaderWidth, int rowHeight, boolean iconEnable, final boolean editable) { this.editable = editable; this.table = new JTable() { private static final long serialVersionUID = 1L; private JPopupMenu pupupMenu = new TablePopupMenu(); @Override public void editingStopped(ChangeEvent e) { if (cellEditWorker != null) { TableCellEditor editor = getCellEditor(); if (editor != null) { Object value = editor.getCellEditorValue(); if (!"".equals(value.toString()) && getEditingRow() == getRowCount() - 1) { cellEditWorker.addNewRow(); } } } super.editingStopped(e); } @Override public Component prepareRenderer(TableCellRenderer tcr, int row, int column) { Component c = super.prepareRenderer(tcr, row, column); if (cellEditWorker != null) { if (!table.isRowSelected(row)) { if (cellEditWorker.isModified(row, column)) { c.setBackground(MODIFIED_COLOR); } else { c.setBackground(null); } } } return c; } @Override public boolean isCellEditable(int row, int column) { if (!editable) { return false; } return super.isCellEditable(row, column); } @Override protected void processMouseEvent(MouseEvent event) { super.processMouseEvent(event); if (event.isPopupTrigger()) { // 右クリックがされた場合 if (!event.isControlDown() && !event.isShiftDown()) { // Ctrl も Shift も押されていない場合 // クリックされた行の取得 Point origin = event.getPoint(); int row = rowAtPoint(origin); if (!isRowSelected(row)) { // この行が選択されていない場合 // すべての選択を解除 clearSelection(); } if (row != -1) { // クリックされた行を選択された状態にする addRowSelectionInterval(row, row); } } this.editingStopped(new ChangeEvent(this)); // ポップアップメニューの表示 this.pupupMenu.show(event.getComponent(), event.getX(), event.getY()); } } }; this.table.addKeyListener(new KeyAdapter() { @Override public void keyPressed(KeyEvent keyevent) { if (clipbordOn) { if (editable) { if (keyevent.isControlDown() && (keyevent.getKeyCode() == 'v' || keyevent .getKeyCode() == 'V')) { pasteRows(); } else if (keyevent.isControlDown() && (keyevent.getKeyCode() == 'x' || keyevent .getKeyCode() == 'X')) { cutRows(); } } if (keyevent.isControlDown() && (keyevent.getKeyCode() == 'c' || keyevent .getKeyCode() == 'C')) { copyRows(); } } } }); this.table.setAutoCreateColumnsFromModel(true); this.table.setAutoResizeMode(JTable.AUTO_RESIZE_OFF); this.headerRenderer = new MultiLineHeaderRenderer(null, iconEnable); this.selectedHeaderRenderer = new MultiLineHeaderRenderer(UIManager .getColor("Table.selectionBackground"), iconEnable); final JTableHeader tableHeader = this.table.getTableHeader(); tableHeader.setReorderingAllowed(false); tableHeader.addMouseMotionListener(new MouseMotionAdapter() { @Override public void mouseMoved(MouseEvent e) { int column = table.getTableHeader().columnAtPoint(e.getPoint()); if (column != -1) { if (headerClickListener != null) { if (mouseOverColumn != column) { if (mouseOverColumn != -1) { TableColumn oldTableColumn = table .getColumnModel().getColumn( mouseOverColumn); oldTableColumn .setHeaderRenderer(headerRenderer); } TableColumn newTableColumn = table.getColumnModel() .getColumn(column); newTableColumn .setHeaderRenderer(selectedHeaderRenderer); tableHeader.repaint(); mouseOverColumn = column; } } } } }); tableHeader.addMouseListener(new MouseAdapter() { @Override public void mouseEntered(MouseEvent e) { int column = table.getTableHeader().columnAtPoint(e.getPoint()); if (column != -1) { if (headerClickListener != null) { mouseOverColumn = column; TableColumn tableColumn = table.getColumnModel() .getColumn(column); tableColumn.setHeaderRenderer(selectedHeaderRenderer); tableHeader.repaint(); } } } @Override public void mouseExited(MouseEvent e) { if (headerClickListener != null) { if (mouseOverColumn != -1) { TableColumn tableColumn = table.getColumnModel() .getColumn(mouseOverColumn); tableColumn.setHeaderRenderer(headerRenderer); mouseOverColumn = -1; } } } @Override public void mouseClicked(MouseEvent e) { int column = table.getTableHeader().columnAtPoint(e.getPoint()); if (headerClickListener != null) { headerClickListener.onHeaderClick(column); } } }); this.table.setRowHeight(rowHeight); this.table.setGridColor(new Color(230, 230, 230)); this.tableModel = new DefaultTableModel(); this.table.setModel(this.tableModel); if (rowHeaderWidth > 0) { final JList rowHeader = new JList() { private static final long serialVersionUID = 1L; @Override protected void processMouseEvent(MouseEvent event) { super.processMouseEvent(event); if (event.isPopupTrigger()) { event.setSource(table); event.translatePoint(-rowHeaderWidth, 0); EventQueue eventQueue = Toolkit.getDefaultToolkit() .getSystemEventQueue(); eventQueue.postEvent(event); } } @Override protected void processKeyEvent(KeyEvent event) { super.processKeyEvent(event); if (event.getID() == KeyEvent.KEY_PRESSED) { if (editable) { if (event.isControlDown() && (event.getKeyCode() == 'v' || event .getKeyCode() == 'V')) { pasteRows(); } else if (event.isControlDown() && (event.getKeyCode() == 'x' || event .getKeyCode() == 'X')) { cutRows(); } } if (event.isControlDown() && (event.getKeyCode() == 'c' || event .getKeyCode() == 'C')) { copyRows(); } } } }; rowHeader.addListSelectionListener(new ListSelectionListener() { public void valueChanged(ListSelectionEvent e) { table.editingStopped(new ChangeEvent(this)); int[] selectedIndices = rowHeader.getSelectedIndices(); table.clearSelection(); for (int i = 0; i < selectedIndices.length; i++) { table.addRowSelectionInterval(selectedIndices[i], selectedIndices[i]); } } }); rowHeader.setFixedCellWidth(rowHeaderWidth); rowHeader.setFixedCellHeight(this.table.getRowHeight()); rowHeader.setCellRenderer(new RowHeaderRenderer(this.table)); rowHeader .setBackground(this.table.getTableHeader().getBackground()); this.listModel = new DefaultListModel(); rowHeader.setModel(this.listModel); this.setRowHeaderView(rowHeader); } this.setViewportView(this.table); this.setPreferredSize(new Dimension(width, height - 10)); } public void addColumnHeader(String value, int width) { TableColumnModel columnModel = this.table.getColumnModel(); int[] oldWidth = new int[columnModel.getColumnCount()]; for (int i = 0; i < columnModel.getColumnCount(); i++) { TableColumn column = columnModel.getColumn(i); oldWidth[i] = column.getPreferredWidth(); } this.tableModel.addColumn(value); for (int i = 0; i < columnModel.getColumnCount(); i++) { TableColumn column = columnModel.getColumn(i); column.setHeaderRenderer(this.headerRenderer); PanelCellEditor cellEditor = this.cellEditorMap.get(i); if (cellEditor != null) { column.setCellEditor(cellEditor); column.setCellRenderer(cellEditor); } else { column.setCellEditor(new CustomCellEditor(this.table)); } if (i == columnModel.getColumnCount() - 1) { column.setPreferredWidth(width); } else { column.setPreferredWidth(oldWidth[i]); } } } public void addRow(final String headerValue, final Object[] values) { if (EventQueue.isDispatchThread()) { if (listModel != null) { listModel.addElement(headerValue); if (listModel.size() >= 2) { listModel.set(listModel.size() - 2, String .valueOf(listModel.size() - 1)); } } tableModel.addRow(values); } else { try { SwingUtilities.invokeAndWait(new Runnable() { public void run() { if (listModel != null) { listModel.addElement(headerValue); if (listModel.size() >= 2) { listModel.set(listModel.size() - 2, String .valueOf(listModel.size() - 1)); } } tableModel.addRow(values); } }); } catch (InterruptedException e) { } catch (InvocationTargetException e) { } } if (cellEditWorker != null) { cellEditWorker.changeRowNum(); } } public void addRow(final int row, final String headerValue, final Object[] values) { if (EventQueue.isDispatchThread()) { if (listModel != null) { listModel.add(row, headerValue); for (int i = row; i < listModel.getSize() - 1; i++) { listModel.set(i, String.valueOf(i + 1)); } listModel.set(listModel.getSize() - 1, "+"); } tableModel.insertRow(row, values); } else { try { SwingUtilities.invokeAndWait(new Runnable() { public void run() { if (listModel != null) { listModel.add(row, headerValue); for (int i = row; i < listModel.getSize() - 1; i++) { listModel.set(i, String.valueOf(i + 1)); } listModel.set(listModel.getSize() - 1, "+"); } tableModel.insertRow(row, values); } }); } catch (InterruptedException e) { } catch (InvocationTargetException e) { } } if (cellEditWorker != null) { cellEditWorker.changeRowNum(); } } public void removeSelectedRows() { if (EventQueue.isDispatchThread()) { int[] rows = table.getSelectedRows(); for (int i = rows.length - 1; i >= 0; i--) { removeRow(rows[i]); } } else { try { SwingUtilities.invokeAndWait(new Runnable() { public void run() { int[] rows = table.getSelectedRows(); for (int i = rows.length - 1; i >= 0; i--) { removeRow(rows[i]); } } }); } catch (InterruptedException e) { } catch (InvocationTargetException e) { } } } public void removeRow(final int row) { if (EventQueue.isDispatchThread()) { tableModel.removeRow(row); if (listModel != null) { listModel.remove(row); for (int i = row; i < listModel.getSize() - 1; i++) { listModel.set(i, String.valueOf(i + 1)); } } } else { try { SwingUtilities.invokeAndWait(new Runnable() { public void run() { tableModel.removeRow(row); if (listModel != null) { listModel.remove(row); for (int i = row; i < listModel.getSize() - 1; i++) { listModel.set(i, String.valueOf(i + 1)); } } } }); } catch (InterruptedException e) { } catch (InvocationTargetException e) { } } if (cellEditWorker != null) { cellEditWorker.changeRowNum(); } } public void removeAllRow() { while (tableModel.getRowCount() > 0) { removeRow(0); } } public void removeData() { try { SwingUtilities.invokeAndWait(new Runnable() { public void run() { if (listModel != null) { listModel.removeAllElements(); } tableModel.setRowCount(0); tableModel.setColumnCount(0); } }); } catch (InterruptedException e) { } catch (InvocationTargetException e) { } if (cellEditWorker != null) { cellEditWorker.changeRowNum(); } } public int getItemCount() { return this.tableModel.getRowCount(); } public int getColumnCount() { return this.tableModel.getColumnCount(); } public Object getValueAt(int row, int column) { return this.tableModel.getValueAt(row, column); } public void setValueAt(Object value, int row, int column) { this.tableModel.setValueAt(value, row, column); } public int[] getSelection() { return this.table.getSelectedRows(); } public void setCellEditWorker(CellEditWorker cellEditWorker) { this.cellEditWorker = cellEditWorker; } /** * カラムテーブルの選択されている部分をコピーします。 */ private void copyToClipboard() { // 選択されている行を取得 int[] selectedRows = this.getSelection(); if (selectedRows.length == 0) { return; } StringBuilder builder = new StringBuilder(); // 全てが選択されている場合はヘッダもコピー // if (selectedRows.length == this.editColumnTable.getItemCount()) { // for (TableColumn c : this.editColumnTable.getColumns()) { // builder.append(c.getText()); // builder.append("\t"); // } // builder.deleteCharAt(builder.length() - 1); // builder.append("\r\n"); // } int columnCount = this.getColumnCount(); for (int selectedRow : selectedRows) { for (int column = 0; column < columnCount; column++) { Object value = this.getValueAt(selectedRow, column); builder.append(Format.toString(value)); builder.append("\t"); } builder.deleteCharAt(builder.length() - 1); builder.append("\r\n"); } Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard(); clipboard.setContents(new StringSelection(builder.toString()), this); } /** * カラムテーブルにクリップボードの内容を貼り付けます。 */ private int pasteFromClipboard(boolean insert) { int count = 0; if (this.getSelection().length == 0) { return 0; } int row = this.getSelection()[0]; // クリップボードから読み込み Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard(); Transferable transferable = clipboard.getContents(this); if (transferable == null || !transferable.isDataFlavorSupported(DataFlavor.stringFlavor)) { return count; } String data; try { data = (String) transferable .getTransferData(DataFlavor.stringFlavor); Scanner scanner = new Scanner(data); Scanner lineScanner = null; while (scanner.hasNextLine()) { String line = scanner.nextLine(); lineScanner = new Scanner(line); lineScanner.useDelimiter("\t"); if (!insert) { if (row == this.getItemCount() - 1) { if (this.cellEditWorker == null) { break; } this.cellEditWorker.addNewRow(); } int column = 0; while (lineScanner.hasNext()) { String text = lineScanner.next(); this.setValueAt(text, row, column++); } } else { List<String> texts = new ArrayList<String>(); while (lineScanner.hasNext()) { String text = lineScanner.next(); texts.add(text); } this.addRow(row, "", texts .toArray(new String[texts.size()])); } row++; count++; } } catch (Exception e) { Activator.showExceptionDialog(e); } return count; } private class TablePopupMenu extends JPopupMenu { private static final long serialVersionUID = 7496925348009972492L; private JMenuItem cutMenu; private JMenuItem deleteMenu; private TablePopupMenu() { FontData fontData = Display.getCurrent().getSystemFont() .getFontData()[0]; Font font = new Font(fontData.getName(), Font.PLAIN, 12); if (clipbordOn) { if (editable) { cutMenu = new JMenuItem(ResourceString .getResourceString("action.title.cut")); cutMenu.setFont(font); this.add(cutMenu); cutMenu.addActionListener(new ActionListener() { /** * 「切り取り」メニュー選択時処理 * * @param even * イベント */ public void actionPerformed(ActionEvent even) { cutRows(); } }); } JMenuItem copyMenu = new JMenuItem(ResourceString .getResourceString("action.title.copy")); copyMenu.setFont(font); this.add(copyMenu); copyMenu.addActionListener(new ActionListener() { /** * 「コピー」メニュー選択時処理 * * @param even * イベント */ public void actionPerformed(ActionEvent even) { copyRows(); } }); if (editable) { JMenuItem pasteMenu = new JMenuItem(ResourceString .getResourceString("action.title.paste")); pasteMenu.setFont(font); this.add(pasteMenu); pasteMenu.addActionListener(new ActionListener() { /** * 「貼り付け」メニュー選択時処理 * * @param even * イベント */ public void actionPerformed(ActionEvent even) { pasteRows(); } }); this.addSeparator(); } } if (editable) { JMenuItem insertMenu = new JMenuItem(ResourceString .getResourceString("action.title.insert")); insertMenu.setFont(font); this.add(insertMenu); insertMenu.addActionListener(new ActionListener() { /** * 「挿入」メニュー選択時処理 * * @param even * イベント */ public void actionPerformed(ActionEvent even) { insertRow(); } }); if (clipbordOn) { JMenuItem insertPasteMenu = new JMenuItem(ResourceString .getResourceString("action.title.insert.and.paste")); insertPasteMenu.setFont(font); this.add(insertPasteMenu); insertPasteMenu.addActionListener(new ActionListener() { /** * 「挿入 して貼り付け」メニュー選択時処理 * * @param even * イベント */ public void actionPerformed(ActionEvent even) { insertAndPasteRows(); } }); } this.deleteMenu = new JMenuItem(ResourceString .getResourceString("label.delete")); deleteMenu.setFont(font); this.add(deleteMenu); deleteMenu.addActionListener(new ActionListener() { /** * 「削除」メニュー選択時処理 * * @param even * イベント */ public void actionPerformed(ActionEvent even) { deleteRows(); } }); } } @Override public void show(Component invoker, int x, int y) { int[] selectedIndexes = getSelection(); if (selectedIndexes.length == 0 || selectedIndexes[selectedIndexes.length - 1] == table .getRowCount() - 1) { if (this.cutMenu != null) { this.cutMenu.setEnabled(false); this.deleteMenu.setEnabled(false); } } else { if (this.cutMenu != null) { this.cutMenu.setEnabled(true); this.deleteMenu.setEnabled(true); } } super.show(invoker, x, y); } } private static class RowHeaderRenderer extends JLabel implements ListCellRenderer { private static final long serialVersionUID = 1L; public RowHeaderRenderer(JTable table) { JTableHeader header = table.getTableHeader(); setOpaque(true); setBorder(UIManager.getBorder("TableHeader.cellBorder")); setHorizontalAlignment(CENTER); setForeground(header.getForeground()); setBackground(header.getBackground()); setFont(header.getFont()); } public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) { setText((value == null) ? "" : value.toString()); return this; } } private static class MultiLineHeaderRenderer extends JList implements TableCellRenderer { private static final long serialVersionUID = 1L; public MultiLineHeaderRenderer(Color backgroundColor, boolean iconEnable) { this.setCellRenderer(new IconListCellRenderer(iconEnable)); setOpaque(true); setForeground(UIManager.getColor("TableHeader.foreground")); if (backgroundColor == null) { backgroundColor = UIManager.getColor("TableHeader.background"); } setBackground(backgroundColor); setBorder(UIManager.getBorder("TableHeader.cellBorder")); JLabel renderer = (JLabel) getCellRenderer(); renderer.setHorizontalAlignment(JLabel.CENTER); } public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { setFont(table.getFont()); String str = (value == null) ? "" : value.toString(); BufferedReader br = new BufferedReader(new StringReader(str)); String line; Vector<String> v = new Vector<String>(); try { while ((line = br.readLine()) != null) { v.addElement(line); } } catch (IOException ex) { } setListData(v); return this; } public static class IconListCellRenderer extends DefaultListCellRenderer { private static final long serialVersionUID = -1712884508057784069L; private static final ImageIcon ICON; static { try { URL iconUrl = RowHeaderTable.class .getResource("/wrench.png"); ICON = new ImageIcon(iconUrl); } catch (Exception e) { e.printStackTrace(); throw new ExceptionInInitializerError(); } } private boolean iconEnable; private IconListCellRenderer(boolean iconEnable) { this.iconEnable = iconEnable; } @Override public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean hasFocus) { JLabel label = (JLabel) super.getListCellRendererComponent( list, value, index, isSelected, hasFocus); if (this.iconEnable && index == 0) { label.setIcon(ICON); } else if (index == 1) { Font font = label.getFont().deriveFont(10.0f); label.setFont(font); label.setForeground(Color.GRAY); } return label; } } } public void setClipbordOn(boolean clipbordOn) { this.clipbordOn = clipbordOn; } public void lostOwnership(Clipboard clipboard, Transferable contents) { } public void setCellEditor(int column, PanelCellEditor cellEditor) { this.cellEditorMap.put(column, cellEditor); } public PanelCellEditor getCellEditor(int column) { return this.cellEditorMap.get(column); } public void setHeaderClickListener(HeaderClickListener headerClickListener) { this.headerClickListener = headerClickListener; } private void copyRows() { // テーブルからクリップボードへコピー copyToClipboard(); } private void cutRows() { // テーブルからクリップボードへコピー copyToClipboard(); removeSelectedRows(); } private void pasteRows() { // 貼り付け int[] selectedRows = getSelection(); if (selectedRows.length == 0) { return; } // クリップボードからテーブルへ貼り付け int count = pasteFromClipboard(false); table.clearSelection(); table.addRowSelectionInterval(selectedRows[0], selectedRows[0] + count - 1); } public void insertRow() { int[] selectedRows = getSelection(); if (selectedRows.length == 0) { return; } addRow(selectedRows[0], "", null); table.clearSelection(); table.addRowSelectionInterval(selectedRows[0], selectedRows[0]); } public void insertAndPasteRows() { int[] selectedRows = getSelection(); if (selectedRows.length == 0) { return; } int count = pasteFromClipboard(true); table.clearSelection(); table.addRowSelectionInterval(selectedRows[0], selectedRows[0] + count - 1); } public void deleteRows() { removeSelectedRows(); } }