001package jmri.jmrit.roster.swing.functiontable; 002 003import java.awt.datatransfer.*; 004import java.io.IOException; 005 006import javax.swing.*; 007 008import org.slf4j.Logger; 009import org.slf4j.LoggerFactory; 010 011import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; 012import jmri.jmrit.roster.RosterEntry; 013import jmri.jmrit.roster.RosterFunctionEntry; 014import jmri.util.datatransfer.RosterEntrySelection; 015import jmri.util.iharder.dnd.URIDrop; 016import jmri.util.swing.EditableResizableImagePanel; 017 018/** 019 * A TransferHandler that handl drag'n drop over the FunctionTable. 020 * It can handle rows drag'n drop but also drag'n drop into the EditableResizableImagePanel in some of the table cells. 021 * 022 * It is used in the FunctionLabelPane. 023 * 024 * <hr> 025 * This file is part of JMRI. 026 * <p> 027 * JMRI is free software; you can redistribute it and/or modify it under the 028 * terms of version 2 of the GNU General Public License as published by the Free 029 * Software Foundation. See the "COPYING" file for a copy of this license. 030 * <p> 031 * JMRI is distributed in the hope that it will be useful, but WITHOUT ANY 032 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 033 * A PARTICULAR PURPOSE. See the GNU General Public License for more details. 034 * 035 * @author Lionel Jeanson 2009-2026 036 */ 037 038public class FunctionTableRowTransferHandler extends TransferHandler { 039 private final JTable table; 040 private final DataFlavor rowFlavor = new DataFlavor(MyTransferData.class, "function table row+rosterentry+rosterfunctionentry"); 041 042 public FunctionTableRowTransferHandler(JTable table) { 043 this.table = table; 044 } 045 046 @Override 047 protected Transferable createTransferable(JComponent c) { 048 return new DataHandler(table.getSelectedRow(), ((FunctionTableModel)table.getModel()).getRosterEntry(), ((FunctionTableModel)table.getModel()).getRosterFunctionEntryAtRow(table.getSelectedRow()), rowFlavor); 049 } 050 051 @Override 052 public boolean canImport(TransferSupport support) { 053 EditableResizableImagePanel impan = FunctionTableMouseListener.getEditableResizableImagePanelAt(table, support.getDropLocation().getDropPoint().x, support.getDropLocation().getDropPoint().y); 054 if (impan != null) { 055 for (DataFlavor df : support.getDataFlavors()) { 056 if (df.isRepresentationClassReader()) { 057 return true; 058 } 059 } 060 return ( support.isDataFlavorSupported(DataFlavor.imageFlavor) || 061 support.isDataFlavorSupported(DataFlavor.javaFileListFlavor) || 062 support.isDataFlavorSupported(DataFlavor.stringFlavor)); 063 } 064 return support.isDrop() && ( support.isDataFlavorSupported(rowFlavor) || support.isDataFlavorSupported(RosterEntrySelection.rosterEntryFlavor)); 065 } 066 067 @Override 068 public int getSourceActions(JComponent c) { 069 return MOVE; 070 } 071 072 @Override 073 @SuppressFBWarnings(value = "BC_UNCONFIRMED_CAST_OF_RETURN_VALUE", justification = "There is an instanceof test just before the cast.") 074 public boolean importData(TransferSupport support) { 075 if ( !canImport(support) || !(support.getDropLocation() instanceof JTable.DropLocation)) { 076 log.debug("TransferSupport() can't do import"); 077 return false; 078 } 079 JTable.DropLocation drop = (JTable.DropLocation) support.getDropLocation(); 080 // is if a drop on an imageicon? 081 EditableResizableImagePanel impan = FunctionTableMouseListener.getEditableResizableImagePanelAt(table, drop.getDropPoint().x, drop.getDropPoint().y); 082 if (impan != null) { 083 if ( URIDrop.importExternalTransferable(support.getTransferable(), impan)) { 084 return true; 085 } 086 log.debug("Drop on image, but not importable as image, trying other possibilities"); 087 } 088 // a RosterEntry? 089 if (support.isDataFlavorSupported(RosterEntrySelection.rosterEntryFlavor)) { 090 log.debug("Dropping a roster entry -> copy all functions"); 091 try { 092 RosterEntry re = RosterEntrySelection.getRosterEntries(support.getTransferable()).get(0); 093 ((FunctionTableModel)table.getModel()).reset(re.getFunctionEntries(), re.getShuntingFunction(), re.getAttribute(FunctionTableModel.FB_ORDER_ATTR)); 094 return true; 095 } catch (UnsupportedFlavorException | IOException | ClassCastException e) { 096 log.error("Could not drag'n drop roster entry.", e); 097 } 098 } 099 // else must be a row 100 try { 101 MyTransferData data = (MyTransferData) support.getTransferable().getTransferData(rowFlavor); 102 int to = drop.getRow(); 103 if ( data.getRosterEntry().getDccLocoAddress().equals( ((FunctionTableModel)table.getModel()).getRosterEntry().getDccLocoAddress() )) { 104 log.debug("Dropping a row from the same roster entry -> reorder"); 105 int from = data.getRow(); 106 if (to < 0 || from == to || from + 1 == to) { 107 log.debug("TransferSupport() can't do import : same indexes"); 108 return false; 109 } 110 ((FunctionTableModel) table.getModel()).moveRow(from, to > from ? to - 1 : to); 111 table.getSelectionModel().setSelectionInterval( to > from ? to - 1 : to, to > from ? to - 1 : to ); 112 } else { 113 log.debug("Dropping a row from another roster entry -> copy function properties"); 114 ((FunctionTableModel)table.getModel()).setRosterFunctionEntryAtRow(data.getRosterFunctionEntry(), to); 115 } 116 return true; 117 } catch (UnsupportedFlavorException | IOException e) { 118 log.debug("importData() Exception : ", e); 119 } 120 log.debug("Couldn't import."); 121 return false; 122 } 123 124 private static class DataHandler implements Transferable { 125 private final MyTransferData transferData; 126 private final DataFlavor flavor; 127 128 DataHandler(Integer row, RosterEntry re, RosterFunctionEntry fe, DataFlavor flavor) { 129 this.transferData = new MyTransferData(row, re, fe); 130 this.flavor = flavor; 131 } 132 133 @Override 134 public DataFlavor[] getTransferDataFlavors() { 135 return new DataFlavor[] { flavor }; 136 } 137 138 @Override 139 public boolean isDataFlavorSupported(DataFlavor f) { 140 return flavor.equals(f); 141 } 142 143 @Override 144 public Object getTransferData(DataFlavor f) 145 throws UnsupportedFlavorException { 146 if (!isDataFlavorSupported(f)) { 147 throw new UnsupportedFlavorException(f); 148 } 149 return transferData; 150 } 151 } 152 153 private static class MyTransferData { 154 private final int row; 155 private final RosterEntry rosterEntry; 156 private final RosterFunctionEntry functionEntry; 157 158 MyTransferData(int r, RosterEntry re, RosterFunctionEntry fe) { 159 row = r; 160 rosterEntry = re; 161 functionEntry = fe; 162 } 163 164 int getRow() { 165 return row; 166 } 167 168 RosterEntry getRosterEntry() { 169 return rosterEntry; 170 } 171 172 RosterFunctionEntry getRosterFunctionEntry() { 173 return functionEntry; 174 } 175 } 176 177 private static final Logger log = LoggerFactory.getLogger(FunctionTableRowTransferHandler.class); 178}