/* * PlayListNode.java * * Copyright (C) 2006-2012 Gabriel Burca (gburca dash virtmus at ebixio dot com) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ package com.ebixio.virtmus; import com.ebixio.util.Log; import com.ebixio.util.WeakPropertyChangeListener; import com.ebixio.virtmus.actions.GoLive; import com.ebixio.virtmus.actions.PlayListDelete; import com.ebixio.virtmus.actions.PlayListRevertAction; import com.ebixio.virtmus.actions.RenameItemAction; import com.ebixio.virtmus.actions.SavePlayListAction; import com.ebixio.virtmus.actions.SongNewAction; import com.ebixio.virtmus.actions.SongOpenAction; import java.awt.datatransfer.DataFlavor; import java.awt.datatransfer.Transferable; import java.awt.datatransfer.UnsupportedFlavorException; import java.awt.dnd.DnDConstants; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.io.File; import java.io.IOException; import java.text.MessageFormat; import java.util.List; import java.util.logging.Level; import javax.swing.Action; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; import org.openide.ErrorManager; import org.openide.actions.PasteAction; import org.openide.actions.ReorderAction; import org.openide.nodes.AbstractNode; import org.openide.nodes.Node; import org.openide.nodes.NodeTransfer; import org.openide.nodes.PropertySupport; import org.openide.nodes.Sheet; import org.openide.util.Exceptions; import org.openide.util.NbBundle; import org.openide.util.WeakListeners; import org.openide.util.actions.SystemAction; import org.openide.util.datatransfer.PasteType; import org.openide.util.lookup.Lookups; /** * * @author Gabriel Burca <gburca dash virtmus at ebixio dot com> */ public class PlayListNode extends AbstractNode implements PropertyChangeListener, ChangeListener, Comparable<PlayListNode> { private final PlayList playList; /** Creates a new instance of PlayListNode * @param playList The PlayList represented by this node. * @param children The Songs in this node's PlayList. */ public PlayListNode(PlayList playList, Songs children) { super (children, Lookups.fixed(new Object[]{playList, children.getIndex()})); this.playList = playList; displayFormat = new MessageFormat("{0}"); setIconBaseWithExtension("com/ebixio/virtmus/resources/PlayListNode.png"); playList.addPropertyChangeListener(PlayList.PROP_NAME, new WeakPropertyChangeListener(this, playList)); playList.addChangeListener(WeakListeners.change(this, playList)); } @Override public Action[] getActions(boolean context) { switch (playList.type) { case Normal: return new Action[] { SystemAction.get(GoLive.class), SystemAction.get(SavePlayListAction.class), SystemAction.get(PlayListRevertAction.class), null, SystemAction.get(SongNewAction.class), SystemAction.get(SongOpenAction.class), null, SystemAction.get(PasteAction.class), null, SystemAction.get(RenameItemAction.class), SystemAction.get(ReorderAction.class), null, SystemAction.get(PlayListDelete.class) }; case Default: return new Action[] { SystemAction.get(GoLive.class), null, SystemAction.get(SongNewAction.class), SystemAction.get(SongOpenAction.class), null, SystemAction.get(PasteAction.class) }; case AllSongs: return new Action[] { SystemAction.get(GoLive.class), null, SystemAction.get(SongNewAction.class), SystemAction.get(SongOpenAction.class), }; default: return new Action[]{}; } } @Override protected Sheet createSheet() { Sheet sheet = Sheet.createDefault(); Sheet.Set set = Sheet.createPropertiesSet(); PlayList pl = getLookup().lookup(PlayList.class); boolean normal = pl.type == PlayList.Type.Normal; try { Property nameProp = new PropertySupport.Reflection<>(pl, String.class, "getName", normal ? "setName" : null); // get/setName Property fileProp = new PropertySupport.Reflection<>(pl, File.class, "getSourceFile", null); // only getSourceFile Property songsProp = new PropertySupport.Reflection<>(pl, Integer.class, "getSongCnt", null); // only getSongCnt Property tagsProp = new PropertySupport.Reflection<>(pl, String.class, "tags"); // get/setTags Property notesProp = new PropertySupport.Reflection<>(pl, String.class, "notes"); // get/setNotes nameProp.setName("Name"); fileProp.setName("Source File"); songsProp.setName("Songs"); tagsProp.setName("Tags"); tagsProp.setShortDescription( NbBundle.getMessage(PlayListTopComponent.class, "CTL_TagsDescription")); notesProp.setName("Notes"); set.put(nameProp); set.put(fileProp); set.put(songsProp); if (normal) { set.put(tagsProp); set.put(notesProp); } } catch (NoSuchMethodException ex) { ErrorManager.getDefault().notify(ex); } sheet.put(set); return sheet; } // <editor-fold defaultstate="collapsed" desc=" Drag-n-drop "> @Override protected void createPasteTypes(Transferable t, List<PasteType> s) { super.createPasteTypes(t, s); //Log.log("PlayListNode::createPasteTypes " + t.toString() + " p:" + playList.getName()); PasteType paste = getDropType(t, DnDConstants.ACTION_COPY, -1); if (paste != null) s.add(paste); } @Override public PasteType getDropType(final Transferable t, final int action, int index) { //Log.log("PlayListNode::getDropType p:" + playList.getName() + " i:" + Integer.toString(index)); //DataFlavor[] flavors = t.getTransferDataFlavors(); if (t.isDataFlavorSupported(SongFlavor.SONG_FLAVOR)) { // Can't paste existing into the AllSongs PlayList if (playList.type == PlayList.Type.AllSongs) return null; return new PasteType() { @Override public Transferable paste() throws IOException { try { Song song = (Song)t.getTransferData(SongFlavor.SONG_FLAVOR); if (song != null) playList.addSong(song); final Node songNode = NodeTransfer.node(t, NodeTransfer.DND_MOVE + NodeTransfer.CLIPBOARD_CUT); if (songNode != null) { //final PlayList source = node.getLookup().lookup(PlayList.class); songNode.destroy(); } } catch (UnsupportedFlavorException ex) { Exceptions.printStackTrace(ex); } return null; } }; } else if (t.isDataFlavorSupported(DataFlavor.javaFileListFlavor)) { // Handle file drops. Convert PDFs into songs, and sets of // images into a single song. return new PasteType() { @Override public Transferable paste() throws IOException { try { Object o = t.getTransferData(DataFlavor.javaFileListFlavor); List<File> fileList = null; if (o instanceof List) { fileList = (List<File>)o; } if (fileList == null || fileList.isEmpty()) return null; Song s = new Song(); for (File f: fileList) { if (! s.addPage(f)) return null; } // Make an educated guess on the song file String saveTo = fileList.get(0).getAbsolutePath(); saveTo = Utils.trimExtension(saveTo, null); if (s.saveAs(new File(saveTo))) { getPlayList().addSong(s); } } catch (UnsupportedFlavorException ex) { Exceptions.printStackTrace(ex); } return null; } }; } else { return null; } } @Override public boolean canCut() { return false; } // Makes no sense to cut a PlayList @Override public boolean canCopy() { return false; } // Makes no sense to copy a PlayList // </editor-fold> // <editor-fold defaultstate="collapsed" desc=" Node name "> @Override public boolean canRename() { return playList.type == PlayList.Type.Normal; } @Override public void setName(String nue) { playList.setName(nue); } @Override public String getName() { return playList.getName(); } @Override public String getDisplayName() { return getName(); } @Override public String getHtmlDisplayName() { String name = super.getDisplayName(); if (playList.isFullyLoaded()) { name = "<font color='!textText'>" + name + "</font>"; } else { name = "<font color='!controlShadow'>" + name + " (loading)</font>"; } if (playList.isDirty()) { name = "<i>" + name + "</i>"; } if (playList.isMissingSongs()) { name = "<b>" + name + "</b>"; } return name; } // </editor-fold> public PlayList getPlayList() { return playList; } public boolean removeSong(Song s) { return this.playList.removeSong(s); } // <editor-fold defaultstate="collapsed" desc=" ChangeListener interface "> @Override public void stateChanged(ChangeEvent e) { fireDisplayNameChange(null, null); } // </editor-fold> // <editor-fold defaultstate="collapsed" desc=" PropertyChangeListener interface "> @Override public void propertyChange(PropertyChangeEvent evt) { if (PlayList.PROP_NAME.equals(evt.getPropertyName())) { String newName = (String)evt.getNewValue(); this.fireDisplayNameChange(null, newName); } } // </editor-fold> @Override public int compareTo(PlayListNode o) { return playList.compareTo(o.playList); } @Override public String toString() { return super.toString() + " PL: " + playList.toString(); } }