001 /** 002 * iFish - An iRiver iHP jukebox database creation tool 003 * 004 * Copyright (C) 2009 Richard "Shred" Körber 005 * http://ifish.shredzone.org 006 * 007 * This program is free software: you can redistribute it and/or modify 008 * it under the terms of the GNU General Public License as published by 009 * the Free Software Foundation, either version 3 of the License, or 010 * (at your option) any later version. 011 * 012 * This program is distributed in the hope that it will be useful, 013 * but WITHOUT ANY WARRANTY; without even the implied warranty of 014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 015 * GNU General Public License for more details. 016 * 017 * You should have received a copy of the GNU General Public License 018 * along with this program. If not, see <http://www.gnu.org/licenses/>. 019 */ 020 package net.shredzone.ifish.gui; 021 022 import java.awt.datatransfer.DataFlavor; 023 import java.awt.datatransfer.Transferable; 024 import java.awt.datatransfer.UnsupportedFlavorException; 025 import java.io.File; 026 import java.io.IOException; 027 import java.util.ArrayList; 028 import java.util.Iterator; 029 030 import javax.swing.JComponent; 031 import javax.swing.JTable; 032 import javax.swing.TransferHandler; 033 034 import net.shredzone.ifish.IFishPane; 035 import net.shredzone.ifish.db.Entry; 036 import net.shredzone.ifish.db.Playlist; 037 038 /** 039 * A transfer handler that allows to order Entry objects by drag and drop. 040 * 041 * @author Richard Körber <dev@shredzone.de> 042 * @version $Id: EntryTransferHandler.java 291 2009-04-28 21:29:27Z shred $ 043 */ 044 public class EntryTransferHandler extends TransferHandler { 045 private static final long serialVersionUID = 4121130320965023281L; 046 private final IFishPane fish; 047 048 /** 049 * Create a new EntryTransferHandler 050 * 051 * @param fish Reference to IFishPane 052 */ 053 public EntryTransferHandler( IFishPane fish ) { 054 this.fish = fish; 055 } 056 057 /** 058 * Get allowed source actions. This TransferHandler only supports MOVE since 059 * we want to sort JTable entries. 060 * 061 * @param c JComponent that initiates the DnD action. 062 * @return Source action, MOVE here. 063 */ 064 @Override 065 public int getSourceActions( JComponent c ) { 066 return MOVE; 067 } 068 069 /** 070 * Check if this TransferHandler accepts one of the given flavors. Only 071 * <code>EntryContainer.entryFlavor</code> is accepted here. 072 * 073 * @param comp JComponent that is the target of the DnD action. 074 * @param transferFlavors DataFlavors to be checked 075 * @return true if this TransferHandler accepts one of the flavors. 076 */ 077 @Override 078 public boolean canImport( JComponent comp, DataFlavor[] transferFlavors ) { 079 for( int ix=0; ix<transferFlavors.length; ix++ ) { 080 if( EntryContainer.entryFlavor.equals( transferFlavors[ix] ) ) 081 return true; 082 } 083 return false; 084 } 085 086 /** 087 * Create a Transferable. The Transferable will contain all Entry 088 * objects that have been selected. 089 * 090 * @param comp JComponent that is the source of the DnD action. 091 * @return Transferable containing the data. 092 */ 093 @Override 094 public Transferable createTransferable( JComponent comp ) { 095 EntryContainer container = new EntryContainer(); 096 097 //--- Fetch all selected Entry --- 098 JTable table = (JTable) comp; 099 PlaylistTableModel model = (PlaylistTableModel) table.getModel(); 100 int[] rows = table.getSelectedRows(); 101 for( int ix=0; ix<rows.length; ix++ ) { 102 container.add( model.getEntryAt( rows[ix] ) ); 103 } 104 105 //--- Create a Transferable --- 106 return new EntryTransferable( container ); 107 } 108 109 /** 110 * Import data after dropping them to the component. 111 * 112 * @param comp JComponent that is the target of the DnD action. 113 * @param t Transferable containing the transported data. 114 * @return Always true. 115 */ 116 @Override 117 public boolean importData( JComponent comp, Transferable t ) { 118 try { 119 EntryContainer container = (EntryContainer) t.getTransferData( EntryContainer.entryFlavor ); 120 121 //--- Get the table and the current position --- 122 JTable table = (JTable) comp; 123 PlaylistTableModel model = (PlaylistTableModel) table.getModel(); 124 Playlist pl = model.getPlaylist(); 125 int pos = table.getSelectedRow(); 126 pos = Math.max( pos, 0 ); 127 128 //--- All all Entry to the table --- 129 for (Entry entry : container) { 130 if( pl.contains(entry) ) { // Only accept existing entries 131 pos = pl.insertEntry( pos, entry ); 132 } 133 } 134 fish.setUnsaved( true ); 135 136 //--- Select the original target row --- 137 // It might have been shifted down in the meantime, so we have 138 // to select it again. 139 table.setRowSelectionInterval( pos,pos ); 140 }catch( Exception e ) {} // do nothing if an exception occured 141 return true; 142 } 143 144 /*----------------------------------------------------------------------------*/ 145 146 /** 147 * An EntryContainer holds a selected list of Entry objects. 148 */ 149 public static class EntryContainer extends ArrayList<Entry> { 150 private static final long serialVersionUID = 3258408422045923383L; 151 152 /** 153 * A DataFlavor used for transporting EntryContainer via clipboard, 154 * drag&drop etc. 155 */ 156 public final static DataFlavor entryFlavor = 157 new DataFlavor( 158 EntryContainer.class, 159 "application/x-java-serialized-object" 160 ); 161 162 /** 163 * Get a string representation. This will be the file name of all Entry 164 * files on the target machine, with one name in each row. 165 * 166 * @return String representation 167 */ 168 @Override 169 public String toString() { 170 StringBuffer buff = new StringBuffer(); 171 String linesepp = System.getProperty("line.separator"); 172 173 for (Entry e : this) { 174 buff.append( e.getFile().toString() ); 175 buff.append( linesepp ); 176 } 177 178 return buff.toString(); 179 } 180 181 } 182 183 /*----------------------------------------------------------------------------*/ 184 185 /** 186 * This is a Transferable that allows to transfer several Entry objects 187 * as Entry itself, as well as a list of Files and a plain string. 188 */ 189 public static class EntryTransferable extends java.awt.datatransfer.StringSelection { 190 private final EntryContainer container; // Container for the Entry objects 191 192 /** 193 * Create a new EntryTransferable. 194 * 195 * @param entries EntryContainer holding the Entry objects 196 */ 197 public EntryTransferable( EntryContainer entries ) { 198 super( entries.toString() ); 199 container = entries; 200 } 201 202 /** 203 * Get all available DataFlavors. This is the <code>EntryContainer.entryFlavor</code> 204 * in first place, then <code>DataFlavor.javaFileListFlavor</code>, 205 * then all DataFlavor given by StringSelection. 206 * 207 * @return Array of all available DataFlavor. 208 */ 209 @Override 210 public DataFlavor[] getTransferDataFlavors() { 211 //--- Get the flavors from the superclass --- 212 DataFlavor[] superflavor = super.getTransferDataFlavors(); 213 214 //--- Add our flavor to the front --- 215 DataFlavor[] result = new DataFlavor[superflavor.length+2]; 216 System.arraycopy( superflavor,0, result,2, superflavor.length ); 217 result[0] = EntryContainer.entryFlavor; 218 result[1] = DataFlavor.javaFileListFlavor; 219 220 //--- Return the new flavor array --- 221 return result; 222 } 223 224 /** 225 * Check if this Transferable supports a certain DataFlavor. 226 * 227 * @param flavor DataFlavor to be checked 228 * @return true if the DataFlavor is supported. 229 */ 230 @Override 231 public boolean isDataFlavorSupported( DataFlavor flavor ) { 232 if( EntryContainer.entryFlavor.equals(flavor) ) return true; 233 if( DataFlavor.javaFileListFlavor.equals(flavor) ) return true; 234 235 return super.isDataFlavorSupported( flavor ); 236 } 237 238 /** 239 * Get the transported data for a certain DataFlavor. For 240 * <code>EntryContainer.entryFlavor</code>, an EntryContainer object 241 * will be returned. 242 * 243 * @param flavor DataFlavor to get the data for 244 * @return Data object for this flavor. 245 * @throws UnsupportedFlavorException if the method was unable to get 246 * a data object for the requested flavor. 247 */ 248 @Override 249 public Object getTransferData( DataFlavor flavor ) 250 throws UnsupportedFlavorException, IOException { 251 //--- EntryFlavor --- 252 // Just return the EntryContainer. 253 if( EntryContainer.entryFlavor.equals(flavor) ) { 254 return container; 255 } 256 257 //--- FileListFlavor --- 258 // Return a list with a File object for each Entry. 259 if( DataFlavor.javaFileListFlavor.equals(flavor) ) { 260 ArrayList<File> lResult = new ArrayList<File>( container.size() ); 261 for (Entry e : container) { 262 lResult.add( e.getFile() ); 263 } 264 return lResult; 265 } 266 267 //--- Otherwise return the super data --- 268 return super.getTransferData( flavor ); 269 } 270 } 271 272 }