package com.revolsys.swing.table; import java.awt.BorderLayout; import java.awt.Component; import java.awt.Point; import java.awt.datatransfer.DataFlavor; import java.awt.datatransfer.StringSelection; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.awt.event.MouseListener; import java.io.Closeable; import java.lang.ref.Reference; import java.lang.ref.WeakReference; import javax.swing.JPanel; import javax.swing.JPopupMenu; import javax.swing.JScrollPane; import javax.swing.JTable; import javax.swing.table.JTableHeader; import javax.swing.table.TableCellEditor; import javax.swing.table.TableModel; import com.revolsys.collection.EmptyReference; import com.revolsys.datatype.DataTypes; import com.revolsys.swing.action.enablecheck.ObjectPropertyEnableCheck; import com.revolsys.swing.dnd.ClipboardUtil; import com.revolsys.swing.menu.MenuFactory; import com.revolsys.swing.toolbar.ToolBar; import com.revolsys.util.Property; public class TablePanel extends JPanel implements MouseListener, Closeable { private static int eventColumn; private static int eventRow; private static Reference<BaseJTable> eventTable = new WeakReference<>(null); private static Reference<MouseEvent> popupMouseEvent = new WeakReference<>(null); private static final long serialVersionUID = 1L; public static int getEventColumn() { return eventColumn; } public static int getEventRow() { return eventRow; } @SuppressWarnings("unchecked") public static <V extends JTable> V getEventTable() { return (V)eventTable.get(); } public static MouseEvent getPopupMouseEvent() { return popupMouseEvent.get(); } protected static void setEventRow(final BaseJTable table, final MouseEvent e) { if (e.getSource() == table) { final Point point = e.getPoint(); eventTable = new WeakReference<>(table); final int eventRow = table.rowAtPoint(point); final int eventColumn = table.columnAtPoint(point); if (eventRow > -1 && eventColumn > -1) { TablePanel.eventRow = table.convertRowIndexToModel(eventRow); TablePanel.eventColumn = table.convertColumnIndexToModel(eventColumn); if (e.getButton() == MouseEvent.BUTTON3) { // table.getSelectionModel().setSelectionInterval(eventRow, eventRow); if (table.isEditing()) { table.getCellEditor().stopCellEditing(); } } } else { TablePanel.eventRow = -1; TablePanel.eventColumn = -1; } } } protected static void setHeaderEventColumn(final BaseJTable table, final JTableHeader tableHeader, final MouseEvent e) { final Object source = e.getSource(); if (source == tableHeader) { final Point point = e.getPoint(); eventTable = new WeakReference<>(table); final int modelColumn = tableHeader.columnAtPoint(point); TablePanel.eventRow = -1; if (modelColumn > -1) { TablePanel.eventColumn = table.convertColumnIndexToModel(modelColumn); } else { TablePanel.eventColumn = -1; } } } private final MouseListener tableHeaderMouseListener = new MouseAdapter() { @Override public void mousePressed(final MouseEvent e) { doHeaderMenu(e); } @Override public void mouseReleased(final MouseEvent e) { doHeaderMenu(e); } }; private JScrollPane scrollPane; private BaseJTable table; private ToolBar toolBar = new ToolBar(); private final MenuFactory headerMenu = new MenuFactory(getClass().getName()); public TablePanel(final BaseJTable table) { super(new BorderLayout()); eventRow = -1; eventColumn = -1; eventTable = new EmptyReference<>(); popupMouseEvent = new EmptyReference<>(); this.table = table; final AbstractTableModel model = (AbstractTableModel)table.getModel(); add(this.toolBar, BorderLayout.NORTH); this.scrollPane = new JScrollPane(table); table.addMouseListener(this); table.getTableHeader().addMouseListener(this.tableHeaderMouseListener); add(this.scrollPane, BorderLayout.CENTER); final MenuFactory menu = model.getMenu(); menu.addMenuItemTitleIcon("dataTransfer", "Copy Field Value", "page_copy", new ObjectPropertyEnableCheck(this, "canCopy"), this::copyFieldValue); menu.addMenuItemTitleIcon("dataTransfer", "Cut Field Value", "cut", this::isCanCut, this::cutFieldValue); menu.addMenuItemTitleIcon("dataTransfer", "Paste Field Value", "paste_plain", new ObjectPropertyEnableCheck(this, "canPaste"), this::pasteFieldValue); } @Override public void close() { if (this.scrollPane != null) { remove(this.scrollPane); this.scrollPane = null; } if (this.table != null) { this.table.removeMouseListener(this); final AbstractTableModel model = getTableModel(); model.dispose(); } this.table = null; if (this.toolBar != null) { remove(this.toolBar); this.toolBar = null; } } private void copyCurrentCell() { final TableModel model = getTableModel(); final Object value = model.getValueAt(eventRow, eventColumn); final String copyValue; if (model instanceof AbstractTableModel) { final AbstractTableModel tableModel = (AbstractTableModel)model; copyValue = tableModel.toCopyValue(eventRow, eventColumn, value); } else { copyValue = DataTypes.toString(value); } final StringSelection transferable = new StringSelection(copyValue); ClipboardUtil.setContents(transferable); } public void copyFieldValue() { if (isEditingCurrentCell()) { if (!this.table.getCellEditor().stopCellEditing()) { return; } } copyCurrentCell(); } public void cutFieldValue() { if (isEditingCurrentCell()) { if (!this.table.getCellEditor().stopCellEditing()) { return; } } copyCurrentCell(); if (isCurrentCellEditable()) { final TableModel tableModel = getTableModel(); tableModel.setValueAt(null, eventRow, eventColumn); } } protected void doHeaderMenu(final MouseEvent e) { setHeaderEventColumn(this.table, this.table.getTableHeader(), e); if (eventColumn > -1 && e.isPopupTrigger()) { e.consume(); final Object menuSource = getHeaderMenuSource(); MenuFactory.setMenuSource(menuSource); final JPopupMenu menu = getHeaderMenu(eventColumn); if (menu != null) { final TableCellEditor cellEditor = this.table.getCellEditor(); if (cellEditor != null) { cellEditor.stopCellEditing(); } popupMouseEvent = new WeakReference<>(e); final int x = e.getX(); final int y = e.getY(); MenuFactory.showMenu(menu, this.table, x, y); } } } protected void doMenu(final MouseEvent e) { setEventRow(this.table, e); if (eventRow > -1 && e.isPopupTrigger()) { e.consume(); final AbstractTableModel tableModel = getTableModel(); final Object menuSource = getMenuSource(); MenuFactory.setMenuSource(menuSource); final JPopupMenu menu = tableModel.getMenu(eventRow, eventColumn); if (menu != null) { final TableCellEditor cellEditor = this.table.getCellEditor(); if (cellEditor == null || cellEditor.stopCellEditing()) { popupMouseEvent = new WeakReference<>(e); final Component component = e.getComponent(); if (component == this.table) { final int x = e.getX(); final int y = e.getY(); MenuFactory.showMenu(menu, this.table, x + 5, y); } else { final int xOnScreen = e.getXOnScreen(); final int yOnScreen = e.getYOnScreen(); final Point locationOnScreen = getLocationOnScreen(); final int x = xOnScreen - locationOnScreen.x; final int y = yOnScreen - locationOnScreen.y; MenuFactory.showMenu(menu, this, x + 5, y); } } } } } public MenuFactory getHeaderMenu() { return this.headerMenu; } public JPopupMenu getHeaderMenu(final int eventColumn) { return this.headerMenu.newJPopupMenu(); } protected Object getHeaderMenuSource() { return null; } protected Object getMenuSource() { return null; } public JScrollPane getScrollPane() { return this.scrollPane; } @SuppressWarnings("unchecked") public <T extends BaseJTable> T getTable() { return (T)this.table; } @SuppressWarnings("unchecked") public <T extends AbstractTableModel> T getTableModel() { return (T)this.table.getModel(); } public ToolBar getToolBar() { return this.toolBar; } public boolean isCanCopy() { return isEditingCurrentCell() || isCurrentCellHasValue(); } public boolean isCanCut() { return isEditingCurrentCell() || isCurrentCellHasValue() && isCurrentCellEditable(); } public boolean isCanPaste() { if (isEditingCurrentCell()) { return true; } else if (isCurrentCellEditable()) { final String value = ClipboardUtil.getContents(DataFlavor.stringFlavor); return Property.hasValue(value); } return false; } public boolean isCurrentCellEditable() { if (eventRow > -1 && eventColumn > -1) { return getTableModel().isCellEditable(eventRow, eventColumn); } return false; } public boolean isCurrentCellHasValue() { if (isEditingCurrentCell()) { return true; } else if (eventRow > -1 && eventColumn > -1) { final TableModel tableModel = getTableModel(); final Object value = tableModel.getValueAt(eventRow, eventColumn); return Property.hasValue(value); } return false; } public boolean isEditing() { return this.table.isEditing(); } public boolean isEditingCurrentCell() { if (isEditing()) { if (eventRow > -1 && eventRow == this.table.getEditingRow()) { if (eventColumn > -1 && eventColumn == this.table.getEditingColumn()) { return true; } } } return false; } @Override public void mouseClicked(final MouseEvent e) { } @Override public void mouseEntered(final MouseEvent e) { } @Override public void mouseExited(final MouseEvent e) { } @Override public void mousePressed(final MouseEvent e) { setEventRow(this.table, e); doMenu(e); } @Override public void mouseReleased(final MouseEvent e) { doMenu(e); } public void pasteFieldValue() { if (isEditingCurrentCell()) { if (!this.table.getCellEditor().stopCellEditing()) { return; } } final String value = ClipboardUtil.getContents(DataFlavor.stringFlavor); if (Property.hasValue(value)) { final TableModel tableModel = getTableModel(); if (tableModel.isCellEditable(eventRow, eventColumn)) { tableModel.setValueAt(value, eventRow, eventColumn); } } } }