Java tutorial
/* * Created by Angel Leon (@gubatron), Alden Torres (aldenml) * Copyright (c) 2011, 2012, FrostWire(R). All rights reserved. * * 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 3 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, see <http://www.gnu.org/licenses/>. */ package com.frostwire.gui.library; import java.awt.event.ActionEvent; import java.awt.event.MouseEvent; import java.io.File; import java.util.ArrayList; import java.util.Arrays; import java.util.HashSet; import java.util.List; import java.util.Set; import javax.swing.AbstractAction; import javax.swing.Action; import javax.swing.DropMode; import javax.swing.JComponent; import javax.swing.JMenu; import javax.swing.JPopupMenu; import javax.swing.JTable; import javax.swing.ListSelectionModel; import javax.swing.event.MouseInputListener; import javax.swing.table.TableCellEditor; import javax.swing.table.TableColumn; import javax.swing.table.TableColumnModel; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.limewire.concurrent.ThreadExecutor; import org.limewire.util.FilenameUtils; import org.limewire.util.StringUtils; import com.frostwire.core.FileDescriptor; import com.frostwire.gui.filters.TableLineFilter; import com.frostwire.gui.player.AudioSource; import com.frostwire.gui.player.DeviceAudioSource; import com.frostwire.gui.player.MediaPlayer; import com.limegroup.gnutella.MediaType; import com.limegroup.gnutella.gui.ButtonRow; import com.limegroup.gnutella.gui.GUIMediator; import com.limegroup.gnutella.gui.I18n; import com.limegroup.gnutella.gui.PaddedPanel; import com.limegroup.gnutella.gui.actions.LimeAction; import com.limegroup.gnutella.gui.actions.SearchAction; import com.limegroup.gnutella.gui.tables.LimeJTable; import com.limegroup.gnutella.gui.themes.SkinMenu; import com.limegroup.gnutella.gui.themes.SkinMenuItem; import com.limegroup.gnutella.gui.themes.SkinPopupMenu; import com.limegroup.gnutella.gui.themes.ThemeMediator; import com.limegroup.gnutella.gui.util.BackgroundExecutorService; import com.limegroup.gnutella.settings.LibrarySettings; import com.limegroup.gnutella.util.QueryUtils; /** * * @author gubatron * @author aldenml * */ public class LibraryDeviceTableMediator extends AbstractLibraryTableMediator<LibraryDeviceTableModel, LibraryDeviceTableDataLine, FileDescriptor> { private static final Log LOG = LogFactory.getLog(LibraryDeviceTableMediator.class); /** * Variables so the PopupMenu & ButtonRow can have the same listeners */ public static Action LAUNCH_ACTION; private Action saveToAction; private Device device; private byte fileType; private FileDescriptorFilter FILE_DESCRIPTOR_FILTER; /** * instance, for singelton access */ private static LibraryDeviceTableMediator INSTANCE; public static LibraryDeviceTableMediator instance() { if (INSTANCE == null) { INSTANCE = new LibraryDeviceTableMediator(); } return INSTANCE; } /** * Build some extra listeners */ protected void buildListeners() { super.buildListeners(); LAUNCH_ACTION = new LaunchAction(); saveToAction = new SaveToAction(); } /** * Set up the constants */ protected void setupConstants() { super.setupConstants(); MAIN_PANEL = new PaddedPanel(); FILE_DESCRIPTOR_FILTER = new FileDescriptorFilter(); DATA_MODEL = new LibraryDeviceTableModel(FILE_DESCRIPTOR_FILTER); TABLE = new LimeJTable(DATA_MODEL); Action[] aa = new Action[] { LAUNCH_ACTION, saveToAction, OPTIONS_ACTION }; BUTTON_ROW = new ButtonRow(aa, ButtonRow.X_AXIS, ButtonRow.NO_GLUE); } // inherit doc comment protected JPopupMenu createPopupMenu() { if (TABLE.getSelectionModel().isSelectionEmpty()) return null; JPopupMenu menu = new SkinPopupMenu(); menu.add(new SkinMenuItem(LAUNCH_ACTION)); menu.add(new SkinMenuItem(saveToAction)); int[] rows = TABLE.getSelectedRows(); menu.addSeparator(); LibraryDeviceTableDataLine line = DATA_MODEL.get(rows[0]); menu.add(createSearchSubMenu(line)); return menu; } private JMenu createSearchSubMenu(LibraryDeviceTableDataLine dl) { JMenu menu = new SkinMenu(I18n.tr("Search")); if (dl != null) { String str = buildQueryString(dl.getInitializeObject()); String keywords = QueryUtils.createQueryString(str); if (keywords.length() > 0) menu.add(new SkinMenuItem(new SearchAction(keywords))); } if (menu.getItemCount() == 0) menu.setEnabled(false); return menu; } private String buildQueryString(FileDescriptor fd) { String str = FilenameUtils.getBaseName(fd.filePath); if (fd.album != null && !fd.album.toLowerCase().contains("unknown")) { str += " " + fd.album; } if (fd.artist != null && !fd.artist.toLowerCase().contains("unknown")) { str += " " + fd.artist; } return str; } /** * Upgrade getScrolledTablePane to public access. */ public JComponent getScrolledTablePane() { return super.getScrolledTablePane(); } /* Don't display anything for this. The LibraryMediator will do it. */ protected void updateSplashScreen() { } /** * Note: This is set up for this to work. * Polling is not needed though, because updates * already generate update events. */ private LibraryDeviceTableMediator() { super("LIBRARY_DEVICE_TABLE"); setMediaType(MediaType.getAnyTypeMediaType()); ThemeMediator.addThemeObserver(this); } /** * Sets up drag & drop for the table. */ protected void setupDragAndDrop() { TABLE.setDragEnabled(true); TABLE.setDropMode(DropMode.INSERT_ROWS); // TABLE.setTransferHandler(new LibraryDeviceTableTransferHandler(this)); } @Override protected void setDefaultRenderers() { super.setDefaultRenderers(); TABLE.setDefaultRenderer(PlayableIconCell.class, new PlayableIconCellRenderer()); TABLE.setDefaultRenderer(PlayableCell.class, new PlayableCellRenderer()); TABLE.setDefaultRenderer(LibraryNameHolder.class, new LibraryNameHolderRenderer()); } protected void setDefaultEditors() { TableColumnModel model = TABLE.getColumnModel(); TableColumn tc; tc = model.getColumn(LibraryDeviceTableDataLine.TITLE_IDX); tc.setCellEditor(new LibraryNameHolderEditor()); } /** * Cancels all editing of fields in the tree and table. */ void cancelEditing() { if (TABLE.isEditing()) { TableCellEditor editor = TABLE.getCellEditor(); editor.cancelCellEditing(); } } /** * Adds the mouse listeners to the wrapped <tt>JTable</tt>. * * @param listener the <tt>MouseInputListener</tt> that handles mouse events * for the library */ void addMouseInputListener(final MouseInputListener listener) { TABLE.addMouseListener(listener); TABLE.addMouseMotionListener(listener); } @Override public void handleMouseDoubleClick(MouseEvent e) { super.handleMouseDoubleClick(e); if (LAUNCH_ACTION.isEnabled()) { LAUNCH_ACTION.actionPerformed(null); } else if (saveToAction.isEnabled()) { saveToAction.actionPerformed(null); } } /** * Updates the Table based on the selection of the given table. * Perform lookups to remove any store files from the shared folder * view and to only display store files in the store view */ void updateTableFiles(final Device device, final byte fileType) { clearTable(); this.device = device; this.fileType = fileType; this.DATA_MODEL.setDevice(device); BackgroundExecutorService.schedule(new Runnable() { @Override public void run() { List<FileDescriptor> fds = device.browse(fileType); if (!LibraryDeviceTableMediator.this.device.equals(device) || LibraryDeviceTableMediator.this.fileType != fileType) { return; // selected another node in the tree } for (int i = 0; i < fds.size(); i++) { final FileDescriptor fileDescriptor = fds.get(i); GUIMediator.safeInvokeLater(new Runnable() { @Override public void run() { addUnsorted(fileDescriptor); } }); } forceResort(); LibraryMediator.instance().getLibraryExplorer().executePendingRunnables(); } }); } /** * Returns the <tt>File</tt> stored at the specified row in the list. * * @param row the row of the desired <tt>File</tt> instance in the * list * * @return a <tt>File</tt> instance associated with the specified row * in the table */ File getFile(int row) { return DATA_MODEL.getFile(row); } /** * Accessor for the table that this class wraps. * * @return The <tt>JTable</tt> instance used by the library. */ JTable getTable() { return TABLE; } ButtonRow getButtonRow() { return BUTTON_ROW; } LibraryDeviceTableDataLine[] getSelectedLibraryLines() { int[] selected = TABLE.getSelectedRows(); LibraryDeviceTableDataLine[] lines = new LibraryDeviceTableDataLine[selected.length]; for (int i = 0; i < selected.length; i++) lines[i] = DATA_MODEL.get(selected[i]); return lines; } /** * Accessor for the <tt>ListSelectionModel</tt> for the wrapped * <tt>JTable</tt> instance. */ ListSelectionModel getSelectionModel() { return TABLE.getSelectionModel(); } public void handleActionKey() { if (fileType == DeviceConstants.FILE_TYPE_AUDIO || fileType == DeviceConstants.FILE_TYPE_RINGTONES) { playSong(); } } private void playSong() { LibraryDeviceTableDataLine line = DATA_MODEL.get(TABLE.getSelectedRow()); if (line == null) { return; } try { String url = device.getDownloadURL(line.getInitializeObject()); AudioSource audioSource = new DeviceAudioSource(url, device, line.getInitializeObject()); if (MediaPlayer.isPlayableFile(audioSource)) { MediaPlayer.instance().asyncLoadMedia(audioSource, true, true, null, getFileView()); } } catch (Throwable e) { LOG.error("Error loading the streaming", e); } } /** * Handles the selection rows in the library window, * enabling or disabling buttons and chat menu items depending on * the values in the selected rows. * * @param row the index of the first row that is selected */ public void handleSelection(int row) { int[] sel = TABLE.getSelectedRows(); if (sel.length == 0) { handleNoSelection(); return; } SEND_TO_FRIEND_ACTION.setEnabled(false); FileDescriptor fd = DATA_MODEL.get(sel[0]).getInitializeObject(); LAUNCH_ACTION.setEnabled(sel.length == 1 && (fileType == DeviceConstants.FILE_TYPE_AUDIO || fileType == DeviceConstants.FILE_TYPE_RINGTONES || fileType == DeviceConstants.FILE_TYPE_VIDEOS) && MediaPlayer.isPlayableFile(fd.filePath)); saveToAction.setEnabled(true); if (sel.length == 1) { LibraryMediator.instance().getLibraryCoverArt().setDefault(); } } /** * Handles the deselection of all rows in the library table, * disabling all necessary buttons and menu items. */ public void handleNoSelection() { LAUNCH_ACTION.setEnabled(false); SEND_TO_FRIEND_ACTION.setEnabled(false); saveToAction.setEnabled(false); } /** * Refreshes the enabledness of the Enqueue button based * on the player enabling state. */ public void setPlayerEnabled(boolean value) { handleSelection(TABLE.getSelectedRow()); } void downloadSelectedItems() { List<AbstractLibraryTableDataLine<FileDescriptor>> selectedLines = getSelectedLines(); List<FileDescriptor> fds = new ArrayList<FileDescriptor>(selectedLines.size()); for (AbstractLibraryTableDataLine<FileDescriptor> line : selectedLines) { fds.add(line.getInitializeObject()); } ThreadExecutor.startThread(new DownloadTask(LibrarySettings.LIBRARY_FROM_DEVICE_DATA_DIR_SETTING.getValue(), device, fds.toArray(new FileDescriptor[0])), "DownloadFromDevice"); } /////////////////////////////////////////////////////// // ACTIONS /////////////////////////////////////////////////////// private final class LaunchAction extends AbstractAction { /** * */ private static final long serialVersionUID = 949208465372392591L; public LaunchAction() { putValue(Action.NAME, I18n.tr("Launch")); putValue(Action.SHORT_DESCRIPTION, I18n.tr("Launch Selected Files")); putValue(LimeAction.ICON_NAME, "LIBRARY_LAUNCH"); } public void actionPerformed(ActionEvent ae) { playSong(); } } private final class SaveToAction extends AbstractAction { private static final long serialVersionUID = 8400749433148927596L; public SaveToAction() { putValue(Action.NAME, I18n.tr("Save")); putValue(LimeAction.SHORT_NAME, I18n.tr("Save")); putValue(Action.SHORT_DESCRIPTION, I18n.tr("Save Selected Files To Folder")); putValue(LimeAction.ICON_NAME, "DEVICE_SAVE"); } public void actionPerformed(ActionEvent e) { downloadSelectedItems(); } } @Override public List<AudioSource> getFileView() { int size = DATA_MODEL.getRowCount(); List<AudioSource> result = new ArrayList<AudioSource>(size); for (int i = 0; i < size; i++) { try { String url = device.getDownloadURL(DATA_MODEL.get(i).getInitializeObject()); result.add(new DeviceAudioSource(url, device, DATA_MODEL.get(i).getInitializeObject())); } catch (Throwable e) { } } return result; } @Override protected void sortAndMaintainSelection(int columnToSort) { super.sortAndMaintainSelection(columnToSort); resetAudioPlayerFileView(); } private void resetAudioPlayerFileView() { // Playlist playlist = AudioPlayer.instance().getCurrentPlaylist(); // if (playlist != null && playlist.equals(currentPlaylist)) { // if (AudioPlayer.instance().getPlaylistFilesView() != null) { // AudioPlayer.instance().setPlaylistFilesView(getFileView()); // } // } } @Override protected AudioSource createAudioSource(LibraryDeviceTableDataLine line) { // TODO Auto-generated method stub return null; } public void filter(String query) { FILE_DESCRIPTOR_FILTER.setQuery(query); DATA_MODEL.filtersChanged(); } @Override public void clearTable() { super.clearTable(); FILE_DESCRIPTOR_FILTER.setQuery(null); } class FileDescriptorFilter implements TableLineFilter<LibraryDeviceTableDataLine> { private String query; private Set<String> tokens; public String getQuery() { return query; } public void setQuery(String query) { if (StringUtils.isNullOrEmpty(query, true) || query.equals(".")) { this.query = null; this.tokens = null; } else { this.query = StringUtils.removeDoubleSpaces(query); this.tokens = new HashSet<String>(Arrays.asList(this.query.toLowerCase().split(" "))); } } @Override public boolean allow(LibraryDeviceTableDataLine node) { if (tokens == null) { return true; } FileDescriptor fd = node.getInitializeObject(); String keywords = (fd.title + " " + fd.artist + " " + fd.album + " " + fd.year).toLowerCase(); boolean foundMatch = true; for (String token : tokens) { if (!keywords.contains(token)) { foundMatch = false; break; } } return foundMatch; } } }