/*
* Jajuk
* Copyright (C) The Jajuk Team
* http://jajuk.info
*
* 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 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
*/
package org.jajuk.ui.views;
import ext.AutoCompleteDecorator;
import java.awt.Color;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.util.HashSet;
import java.util.List;
import java.util.Properties;
import java.util.Set;
import javax.swing.Action;
import javax.swing.ActionMap;
import javax.swing.BorderFactory;
import javax.swing.InputMap;
import javax.swing.JComboBox;
import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.JMenuItem;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.JTextField;
import javax.swing.KeyStroke;
import javax.swing.SortOrder;
import javax.swing.SwingUtilities;
import javax.swing.SwingWorker;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.event.TableModelEvent;
import javax.swing.event.TableModelListener;
import javax.swing.table.TableColumn;
import net.miginfocom.swing.MigLayout;
import org.jajuk.base.AlbumArtistManager;
import org.jajuk.base.ArtistManager;
import org.jajuk.base.File;
import org.jajuk.base.GenreManager;
import org.jajuk.base.Item;
import org.jajuk.base.ItemManager;
import org.jajuk.events.JajukEvent;
import org.jajuk.events.JajukEvents;
import org.jajuk.events.ObservationManager;
import org.jajuk.services.players.QueueModel;
import org.jajuk.ui.actions.ActionManager;
import org.jajuk.ui.actions.JajukActions;
import org.jajuk.ui.helpers.FontManager;
import org.jajuk.ui.helpers.FontManager.JajukFont;
import org.jajuk.ui.helpers.ILaunchCommand;
import org.jajuk.ui.helpers.JajukTableModel;
import org.jajuk.ui.helpers.PlayHighlighterPredicate;
import org.jajuk.ui.helpers.PreferencesJMenu;
import org.jajuk.ui.helpers.TableTransferHandler;
import org.jajuk.ui.helpers.TwoStepsDisplayable;
import org.jajuk.ui.widgets.InformationJPanel;
import org.jajuk.ui.widgets.JajukTable;
import org.jajuk.ui.widgets.JajukToggleButton;
import org.jajuk.util.Conf;
import org.jajuk.util.Const;
import org.jajuk.util.IconLoader;
import org.jajuk.util.JajukIcons;
import org.jajuk.util.Messages;
import org.jajuk.util.UtilFeatures;
import org.jajuk.util.UtilGUI;
import org.jajuk.util.error.CannotRenameException;
import org.jajuk.util.error.JajukException;
import org.jajuk.util.error.NoneAccessibleFileException;
import org.jajuk.util.log.Log;
import org.jdesktop.swingx.autocomplete.ComboBoxCellEditor;
import org.jdesktop.swingx.decorator.ColorHighlighter;
import org.jdesktop.swingx.table.DefaultTableColumnModelExt;
import org.jdesktop.swingx.table.TableColumnExt;
/**
* Abstract table view : common implementation for both files and tracks table
* views.
*/
public abstract class AbstractTableView extends ViewAdapter implements ActionListener,
ItemListener, TableModelListener, TwoStepsDisplayable, ListSelectionListener {
/** Generated serialVersionUID. */
private static final long serialVersionUID = -4418626517605128694L;
JajukTable jtable;
JPanel jpControl;
JajukToggleButton jtbEditable;
private JLabel jlFilter;
private JComboBox jcbProperty;
private JLabel jlEquals;
private JTextField jtfValue;
/** Table model. */
JajukTableModel model;
/** Currently applied filter. */
String sAppliedFilter = "";
/** Currently applied criteria. */
String sAppliedCriteria;
/** Do search panel need a search. */
private boolean bNeedSearch = false;
/** Default time in ms before launching a search automatically. */
private static final int WAIT_TIME = 200;
/** Date last key pressed. */
private long lDateTyped;
/** Editable table configuration name, must be overwritten by child classes. */
String editableConf;
/** Columns to show table configuration name, must be overwritten by child classes. */
String columnsConf;
private JMenuItem jmiPlay;
private JMenuItem jmiPush;
private JMenuItem jmiFrontPush;
JMenuItem jmiDelete;
private JMenuItem jmiPlayRepeat;
private JMenuItem jmiPlayShuffle;
JMenuItem jmiBookmark;
JMenuItem jmiProperties;
JMenuItem jmiFileCopyURL;
PreferencesJMenu pjmTracks;
/** The table/tree sync toggle button. */
JajukToggleButton jtbSync;
private volatile boolean bStopThread = false;
/** Launches a thread used to perform dynamic filtering when user is typing. */
private Thread filteringThread = new Thread("Dynamic user input filtering thread") {
@Override
public void run() {
while (!bStopThread) {
try {
Thread.sleep(200);
} catch (InterruptedException ie) {
Log.error(ie);
}
try {
if (bNeedSearch && (System.currentTimeMillis() - lDateTyped >= WAIT_TIME)) {
sAppliedFilter = jtfValue.getText();
sAppliedCriteria = getApplyCriteria();
applyFilter(sAppliedCriteria, sAppliedFilter);
bNeedSearch = false;
}
} catch (Exception ie) {
Log.error(ie);
}
}
}
};
/**
* Gets the apply criteria.
*
* @return Applied criteria
*/
private String getApplyCriteria() {
int indexCombo = jcbProperty.getSelectedIndex();
if (indexCombo == 0) { // first criteria is special: any
sAppliedCriteria = XML_ANY;
} else { // otherwise, take criteria from model
sAppliedCriteria = model.getIdentifier(indexCombo);
}
return sAppliedCriteria;
}
/**
* Code used in child class SwingWorker for long delay computations (used in
* initUI()).
*
* @return the object
*/
@Override
public Object longCall() {
model = populateTable();
return null;
}
/**
* Code used in child class SwingWorker for display computations (used in
* initUI()).
*
* @param in
*/
@Override
public void shortCall(Object in) {
// Add generic menus
jmiPlay = new JMenuItem(ActionManager.getAction(JajukActions.PLAY_SELECTION));
jmiPlay.putClientProperty(Const.DETAIL_SELECTION, jtable.getSelection());
jtable.getMenu().add(jmiPlay);
jmiFrontPush = new JMenuItem(ActionManager.getAction(JajukActions.PUSH_FRONT_SELECTION));
jmiFrontPush.putClientProperty(Const.DETAIL_SELECTION, jtable.getSelection());
jtable.getMenu().add(jmiFrontPush);
jmiPush = new JMenuItem(ActionManager.getAction(JajukActions.PUSH_SELECTION));
jmiPush.putClientProperty(Const.DETAIL_SELECTION, jtable.getSelection());
jtable.getMenu().add(jmiPush);
jmiPlayRepeat = new JMenuItem(ActionManager.getAction(JajukActions.PLAY_REPEAT_SELECTION));
jmiPlayRepeat.putClientProperty(Const.DETAIL_SELECTION, jtable.getSelection());
jtable.getMenu().add(jmiPlayRepeat);
jmiPlayShuffle = new JMenuItem(ActionManager.getAction(JajukActions.PLAY_SHUFFLE_SELECTION));
jmiPlayShuffle.putClientProperty(Const.DETAIL_SELECTION, jtable.getSelection());
jtable.getMenu().add(jmiPlayShuffle);
jtable.getMenu().addSeparator();
jmiDelete = new JMenuItem(ActionManager.getAction(JajukActions.DELETE));
jmiDelete.putClientProperty(Const.DETAIL_SELECTION, jtable.getSelection());
jtable.getMenu().add(jmiDelete);
jmiFileCopyURL = new JMenuItem(ActionManager.getAction(JajukActions.COPY_TO_CLIPBOARD));
jmiFileCopyURL.putClientProperty(Const.DETAIL_CONTENT, jtable.getSelection());
jtable.getMenu().add(jmiFileCopyURL);
jmiBookmark = new JMenuItem(ActionManager.getAction(JajukActions.BOOKMARK_SELECTION));
jmiBookmark.putClientProperty(Const.DETAIL_SELECTION, jtable.getSelection());
jmiProperties = new JMenuItem(ActionManager.getAction(JajukActions.SHOW_PROPERTIES));
jmiProperties.putClientProperty(Const.DETAIL_SELECTION, jtable.getSelection());
pjmTracks = new PreferencesJMenu(jtable.getSelection());
// Set a default behavior for double click or click on the play column
jtable.setCommand(new ILaunchCommand() {
@Override
public void launch(int nbClicks) {
// Ignore event if several rows are selected
if (jtable.getSelectedColumnCount() != 1) {
return;
}
int iSelectedCol = jtable.getSelectedColumn();
// Convert column selection as columns may have been moved
iSelectedCol = jtable.convertColumnIndexToModel(iSelectedCol);
// We launch the selection :
// - In any case if user clicked on the play column (column 0)
// - Or in case of double click on any column when table is not editable
if (iSelectedCol == 0 || // click on play icon
// double click on any column and edition state false
(nbClicks == 2 && !jtbEditable.isSelected())) {
Item item = model.getItemAt(jtable.convertRowIndexToModel(jtable.getSelectedRow()));
List<File> files = UtilFeatures.getPlayableFiles(item);
if (files.size() > 0) {
// launch it
QueueModel.push(
UtilFeatures.createStackItems(UtilFeatures.applyPlayOption(files),
Conf.getBoolean(Const.CONF_STATE_REPEAT), true),
Conf.getBoolean(Const.CONF_OPTIONS_PUSH_ON_CLICK));
} else {
Messages.showErrorMessage(10);
}
}
}
});
// Control panel
jpControl = new JPanel();
jpControl.setBorder(BorderFactory.createEtchedBorder());
// Create the sync toggle button and restore its state
jtbSync = new JajukToggleButton(ActionManager.getAction(JajukActions.SYNC_TREE_TABLE));
jtbSync.putClientProperty(Const.DETAIL_VIEW, getID());
jtbSync.setSelected(Conf.getBoolean(Const.CONF_SYNC_TABLE_TREE + "." + getID()));
createGenericGUI(jtbSync);
ColorHighlighter colorHighlighter = new ColorHighlighter(new PlayHighlighterPredicate(jtable),
Color.ORANGE, null);
jtable.addHighlighter(colorHighlighter);
// refresh columns conf in case of some attributes been removed
// or added before view instantiation
Properties properties = ObservationManager
.getDetailsLastOccurence(JajukEvents.CUSTOM_PROPERTIES_ADD);
JajukEvent event = new JajukEvent(JajukEvents.CUSTOM_PROPERTIES_ADD, properties);
update(event);
initTable(); // perform type-specific init
// Register keystrokes
setKeystrokes();
}
/**
* Generic part of the panel
* @param component the component to display before filter
*/
void createGenericGUI(JComponent component) {
jtbEditable = new JajukToggleButton(IconLoader.getIcon(JajukIcons.EDIT));
jtbEditable.setToolTipText(Messages.getString("AbstractTableView.11"));
jtbEditable.addActionListener(this);
jlFilter = new JLabel(Messages.getString("AbstractTableView.0"));
// properties combo box, fill with columns names expect ID
jcbProperty = new JComboBox();
// "any" criteria
jcbProperty.addItem(Messages.getString("AbstractTableView.8"));
for (int i = 1; i < model.getColumnCount(); i++) {
// Others columns except ID
jcbProperty.addItem(model.getColumnName(i));
}
jcbProperty.setToolTipText(Messages.getString("AbstractTableView.1"));
jcbProperty.addItemListener(this);
jlEquals = new JLabel(Messages.getString("AbstractTableView.7"));
jtfValue = new JTextField();
jtfValue.setFont(FontManager.getInstance().getFont(JajukFont.SEARCHBOX));
jtfValue.setMargin(new Insets(0, 3, 0, 0));
jtfValue.setForeground(new Color(172, 172, 172));
jtfValue.addKeyListener(new KeyAdapter() {
@Override
public void keyReleased(KeyEvent e) {
bNeedSearch = true;
lDateTyped = System.currentTimeMillis();
// Start filtering thread
if (!filteringThread.isAlive()) {
filteringThread.start();
}
}
});
// Add a focus listener to select all the text and ease previous text cleanup
jtfValue.addFocusListener(new FocusListener() {
@Override
public void focusLost(FocusEvent e) {
jtfValue.setCaretPosition(jtfValue.getText().length());
}
@Override
public void focusGained(FocusEvent e) {
jtfValue.selectAll();
}
});
jtfValue.setToolTipText(Messages.getString("AbstractTableView.3"));
jpControl.setLayout(new MigLayout("insets 5", "[][][grow,gp 70][grow]"));
jpControl.add(jtbEditable, "gapleft 5");
jpControl.add(component, "gapright 15");
jpControl.add(jlFilter, "split 2");
jpControl.add(jcbProperty, "grow,gapright 15");
jpControl.add(jlEquals, "split 2");
jpControl.add(jtfValue, "grow,gapright 2");
setCellEditors();
JScrollPane jsp = new JScrollPane(jtable);
setLayout(new MigLayout("ins 0", "[grow]", "[][grow]"));
add(jpControl, "wrap,grow");
add(jsp, "grow");
jtable.setTransferHandler(new TableTransferHandler(jtable));
jtable.showColumns(jtable.getColumnsConf());
applyFilter(null, null);
jtable.getSelectionModel().addListSelectionListener(this);
// Register on the list for subject we are interested in
ObservationManager.register(this);
// Register keystrokes
setKeystrokes();
}
/*
* (non-Javadoc)
*
* @see org.jajuk.events.Observer#getRegistrationKeys()
*/
@Override
public Set<JajukEvents> getRegistrationKeys() {
Set<JajukEvents> eventSubjectSet = new HashSet<JajukEvents>();
eventSubjectSet.add(JajukEvents.DEVICE_MOUNT);
eventSubjectSet.add(JajukEvents.DEVICE_UNMOUNT);
eventSubjectSet.add(JajukEvents.DEVICE_REFRESH);
eventSubjectSet.add(JajukEvents.CUSTOM_PROPERTIES_ADD);
eventSubjectSet.add(JajukEvents.CUSTOM_PROPERTIES_REMOVE);
eventSubjectSet.add(JajukEvents.RATE_CHANGED);
eventSubjectSet.add(JajukEvents.PARAMETERS_CHANGE);
eventSubjectSet.add(JajukEvents.VIEW_REFRESH_REQUEST);
eventSubjectSet.add(JajukEvents.TREE_SELECTION_CHANGED);
return eventSubjectSet;
}
/**
* Apply a filter, to be implemented by files and tracks tables, alter the
* model.
*
* @param sPropertyName
* @param sPropertyValue
*/
public void applyFilter(final String sPropertyName, final String sPropertyValue) {
SwingWorker<Void, Void> sw = new SwingWorker<Void, Void>() {
@Override
public Void doInBackground() {
model.removeTableModelListener(AbstractTableView.this);
model.populateModel(sPropertyName, sPropertyValue, jtable.getColumnsConf());
model.addTableModelListener(AbstractTableView.this);
return null;
}
@Override
public void done() {
int[] selection = jtable.getSelectedRows();
// Force table repaint (for instance for rating stars update)
model.fireTableDataChanged();
// Restore selection (even if rows content may have change) if is is not now out of bound
boolean outOfBounds = false;
for (int index : selection) {
if (index >= model.getRowCount()) {
outOfBounds = true;
break;
}
}
if (!outOfBounds) {
jtable.setSelectedRows(selection);
}
UtilGUI.stopWaiting();
}
};
UtilGUI.waiting();
sw.execute();
}
/*
* (non-Javadoc)
*
* @see org.jajuk.ui.Observer#update(java.lang.String)
*/
@Override
public void update(final JajukEvent event) {
SwingUtilities.invokeLater(new Runnable() {
@Override
@SuppressWarnings("unchecked")
public void run() {
try {
jtable.setAcceptColumnsEvents(false); // flag reloading to avoid wrong
// column
// events
JajukEvents subject = event.getSubject();
if (JajukEvents.DEVICE_MOUNT.equals(subject)
|| JajukEvents.DEVICE_UNMOUNT.equals(subject)) {
jtable.clearSelection();
// force filter to refresh
applyFilter(sAppliedCriteria, sAppliedFilter);
} else if (JajukEvents.TREE_SELECTION_CHANGED.equals(subject)) {
// Check if the sync tree table option is set for this tree
if (jtbSync.isSelected()) {
// Consume only events from the same perspective and different view
// (for table/tree synchronization)
Properties details = event.getDetails();
if (details != null) {
String sourcePerspective = details.getProperty(Const.DETAIL_PERSPECTIVE);
String sourceView = details.getProperty(Const.DETAIL_VIEW);
if (!(sourcePerspective.equals(getPerspective().getID()))
|| sourceView.equals(getID())) {
return;
}
}
// Update model tree selection
model.setTreeSelection((Set<Item>) details.get(Const.DETAIL_SELECTION));
// force redisplay to apply the filter
jtable.clearSelection();
// force filter to refresh
applyFilter(sAppliedCriteria, sAppliedFilter);
}
} else if (JajukEvents.PARAMETERS_CHANGE.equals(subject)) {
// force redisplay to apply the filter
jtable.clearSelection();
// force filter to refresh
applyFilter(sAppliedCriteria, sAppliedFilter);
} else if (JajukEvents.DEVICE_REFRESH.equals(subject)) {
// force filter to refresh
applyFilter(sAppliedCriteria, sAppliedFilter);
} else if (JajukEvents.VIEW_REFRESH_REQUEST.equals(subject)) {
// force filter to refresh if the events has been triggered by the
// table itself after a column change
JTable table = (JTable) event.getDetails().get(Const.DETAIL_CONTENT);
if (table.equals(jtable)) {
applyFilter(sAppliedCriteria, sAppliedFilter);
}
} else if (JajukEvents.RATE_CHANGED.equals(subject)) {
// Keep current selection and nb of rows
int[] selection = jtable.getSelectedRows();
// force filter to refresh
applyFilter(sAppliedCriteria, sAppliedFilter);
jtable.setSelectedRows(selection);
} else if (JajukEvents.CUSTOM_PROPERTIES_ADD.equals(subject)) {
Properties properties = event.getDetails();
if (properties == null) {
// can be null at view populate
return;
}
model = populateTable();
model.addTableModelListener(AbstractTableView.this);
jtable.setModel(model);
setCellEditors();
// add new item in configuration columns
jtable.addColumnIntoConf((String) properties.get(Const.DETAIL_CONTENT));
jtable.showColumns(jtable.getColumnsConf());
applyFilter(sAppliedCriteria, sAppliedFilter);
jcbProperty.addItem(properties.get(Const.DETAIL_CONTENT));
} else if (JajukEvents.CUSTOM_PROPERTIES_REMOVE.equals(subject)) {
Properties properties = event.getDetails();
if (properties == null) { // can be null at view
// populate
return;
}
// remove item from configuration columns
model = populateTable();// create a new model
model.addTableModelListener(AbstractTableView.this);
jtable.setModel(model);
setCellEditors();
jtable.addColumnIntoConf((String) properties.get(Const.DETAIL_CONTENT));
jtable.showColumns(jtable.getColumnsConf());
applyFilter(sAppliedCriteria, sAppliedFilter);
jcbProperty.removeItem(properties.get(Const.DETAIL_CONTENT));
}
} catch (Exception e) {
Log.error(e);
} finally {
jtable.setAcceptColumnsEvents(true); // make sure to remove this flag
}
}
});
}
/**
* Add keystroke support.
*/
private void setKeystrokes() {
jtable.putClientProperty(Const.DETAIL_SELECTION, jtable.getSelection());
InputMap inputMap = jtable.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
ActionMap actionMap = jtable.getActionMap();
// Delete
Action action = ActionManager.getAction(JajukActions.DELETE);
inputMap.put(KeyStroke.getKeyStroke("DELETE"), "delete");
actionMap.put("delete", action);
// Properties ALT/ENTER
action = ActionManager.getAction(JajukActions.SHOW_PROPERTIES);
inputMap.put(KeyStroke.getKeyStroke("alt ENTER"), "properties");
actionMap.put("properties", action);
}
/**
* Fill the table.
*
* @return the jajuk table model
*/
abstract JajukTableModel populateTable();
/**
* Sets the cell editors.
*/
private void setCellEditors() {
for (TableColumn tc : ((DefaultTableColumnModelExt) jtable.getColumnModel()).getColumns(true)) {
TableColumnExt col = (TableColumnExt) tc;
String sIdentifier = model.getIdentifier(col.getModelIndex());
// create a combo box for genres, note that we can't add new
// genres dynamically
if (Const.XML_GENRE.equals(sIdentifier)) {
JComboBox jcb = new JComboBox(GenreManager.getInstance().getGenresList());
jcb.setEditable(true);
AutoCompleteDecorator.decorate(jcb);
col.setCellEditor(new ComboBoxCellEditor(jcb));
col.setSortable(true);
}
// create a combo box for artists, note that we can't add new
// artists dynamically
else if (Const.XML_ARTIST.equals(sIdentifier)) {
JComboBox jcb = new JComboBox(ArtistManager.getArtistsList());
jcb.setEditable(true);
AutoCompleteDecorator.decorate(jcb);
col.setCellEditor(new ComboBoxCellEditor(jcb));
}
// Same for for album-artists
else if (Const.XML_ALBUM_ARTIST.equals(sIdentifier)) {
JComboBox jcb = new JComboBox(AlbumArtistManager.getAlbumArtistsList());
jcb.setEditable(true);
AutoCompleteDecorator.decorate(jcb);
col.setCellEditor(new ComboBoxCellEditor(jcb));
}
// create a button for playing
else if (Const.XML_PLAY.equals(sIdentifier)) {
col.setMinWidth(PLAY_COLUMN_SIZE);
col.setMaxWidth(PLAY_COLUMN_SIZE);
} else if (Const.XML_TRACK_RATE.equals(sIdentifier)) {
col.setMinWidth(RATE_COLUMN_SIZE);
col.setMaxWidth(RATE_COLUMN_SIZE);
}
}
}
/**
* Detect property change.
*
* @param ie
*/
@Override
public void itemStateChanged(ItemEvent ie) {
if (ie.getSource() == jcbProperty) {
sAppliedFilter = jtfValue.getText();
sAppliedCriteria = getApplyCriteria();
applyFilter(sAppliedCriteria, sAppliedFilter);
}
}
/*
* (non-Javadoc)
*
* @seejavax.swing.event.TableModelListener#tableChanged(javax.swing.event. TableModelEvent)
*/
@Override
public void tableChanged(TableModelEvent e) {
// Check the table change event has not been generated by a
// fireModelDataChange call
if (e.getColumn() < 0) {
return;
}
String sKey = model.getIdentifier(e.getColumn());
Object oValue = model.getValueAt(e.getFirstRow(), e.getColumn());
/* can be Boolean or String */
Item item = model.getItemAt(e.getFirstRow());
try {
// file filter used by physical table view to change only the
// file, not all files associated with the track
Set<File> filter = null;
if (item instanceof File) {
filter = new HashSet<File>();
filter.add((File) item);
}
Item itemNew = ItemManager.changeItem(item, sKey, oValue, filter);
model.setItemAt(e.getFirstRow(), itemNew); // update model
// user message
InformationJPanel.getInstance().setMessage(
Messages.getString("PropertiesWizard.8") + ": " + Messages.getHumanPropertyName(sKey),
InformationJPanel.MessageType.INFORMATIVE);
// Require refresh of all tables
Properties properties = new Properties();
properties.put(Const.DETAIL_ORIGIN, AbstractTableView.this);
// No real device change if Webradio view
if (!(this instanceof WebRadioView)) {
ObservationManager.notify(new JajukEvent(JajukEvents.DEVICE_REFRESH, properties));
}
} catch (NoneAccessibleFileException none) {
Messages.showErrorMessage(none.getCode());
((JajukTableModel) jtable.getModel()).undo(e.getFirstRow(), e.getColumn());
} catch (CannotRenameException cre) {
Messages.showErrorMessage(cre.getCode());
((JajukTableModel) jtable.getModel()).undo(e.getFirstRow(), e.getColumn());
} catch (JajukException je) {
Log.error("104", je);
Messages.showErrorMessage(104, je.getMessage());
((JajukTableModel) jtable.getModel()).undo(e.getFirstRow(), e.getColumn());
}
}
/*
* (non-Javadoc)
*
* @see java.awt.event.ActionListener#actionPerformed(java.awt.event.ActionEvent)
*/
@Override
public void actionPerformed(final ActionEvent e) {
// Editable state
if (e.getSource() == jtbEditable) {
Conf.setProperty(editableConf, Boolean.toString(jtbEditable.isSelected()));
model.setEditable(jtbEditable.isSelected());
return;
}
}
/* (non-Javadoc)
* @see org.jajuk.ui.views.ViewAdapter#cleanup()
*/
@Override
public void cleanup() {
// stop the thread that is waiting for input
if (filteringThread != null) {
bStopThread = true;
try {
filteringThread.join();
filteringThread = null;
} catch (InterruptedException e) {
Log.error(e);
}
}
super.cleanup();
}
/**
* Called when table selection changed.
*
* @param e the List selection event
*/
@Override
public void valueChanged(ListSelectionEvent e) {
if (e.getValueIsAdjusting()) {
// leave during normal refresh
return;
}
// Ignore event if the model is refreshing
if (((JajukTableModel) jtable.getModel()).isRefreshing()) {
return;
}
// We absolutely need to perform the actual treatment in the next EDT call because otherwise,
// the selection is wrong because the selection event is catch first here and after in the JajukTable
// valueChanged() that performs the actual selection computation. Doing this invokeLater ensure to
// serialize the event treatment in the correct order.
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
// Call view specific behavior on selection change
onSelectionChange();
// Hide the copy url if several items selection. Do not simply disable them
// as the getMenu() method enable all menu items
jmiFileCopyURL.setVisible(jtable.getSelectedRowCount() < 2);
// Compute Information view message
if (AbstractTableView.this instanceof TracksTableView) {
int rows = jtable.getSelection().size();
StringBuilder sbOut = new StringBuilder().append(rows).append(
Messages.getString("TracksTreeView.31"));
InformationJPanel.getInstance().setSelection(sbOut.toString());
} else if (AbstractTableView.this instanceof FilesTableView) {
// Compute recursive selection size, nb of items...
long lSize = 0l;
int items = 0;
for (Item item : jtable.getSelection()) {
if (item instanceof File) {
lSize += ((File) item).getSize();
}
}
items = jtable.getSelection().size();
lSize /= 1048576; // set size in MB
StringBuilder sbOut = new StringBuilder().append(items).append(
Messages.getString("FilesTreeView.52"));
if (lSize > 1024) { // more than 1024 MB -> in GB
sbOut.append(lSize / 1024).append('.').append(lSize % 1024)
.append(Messages.getString("FilesTreeView.53"));
} else {
sbOut.append(lSize).append(Messages.getString("FilesTreeView.54"));
}
InformationJPanel.getInstance().setSelection(sbOut.toString());
}
// Refresh the preference menu according to the selection
// (Useless for WebRadioView)
if (!(AbstractTableView.this instanceof WebRadioView)) {
pjmTracks.resetUI(jtable.getSelection());
}
}
});
}
/**
* Callback method called on table selection change.
*/
void onSelectionChange() {
// Do nothing by default
}
/**
* Table initialization after table display.
* Default implementation for table initialization :
* update editable button state.
*
*/
void initTable() {
jtbEditable.setSelected(Conf.getBoolean(editableConf));
// Sort by name by default.
jtable.setSortOrder(1, SortOrder.ASCENDING);
}
}