/**
* ======================== legal notice ======================
*
* File: MCC.java Created: 13. June 2012, 04:57 Author: Geronimo Project: cmpc - a java frontend
* (client) part of compound media player uses external players to play the
* media
*
* CMP - compound media player
*
* is a client/server mediaplayer intended to play any media from any
* workstation without the need to export or mount shares. cmps is an easy to
* use backend with a (ready to use) HTML-interface. Additionally the backend
* supports authentication via HTTP-digest authorization. cmpc is a client with
* vdr-like osd-menues.
*
* Copyright (c) 2012 Reinhard Mantey, some rights reserved! published under
* Creative Commons by-sa For details see
* http://creativecommons.org/licenses/by-sa/3.0/
*
* The cmp project's homepage is at
* http://projects.vdr-developer.org/projects/cmp
*
* --------------------------------------------------------------
*/
package de.schwarzrot.control.app;
import java.awt.Dimension;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.Point;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.BufferedReader;
import java.io.File;
import java.io.InputStreamReader;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import javax.swing.AbstractAction;
import javax.swing.DropMode;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JSeparator;
import javax.swing.JTable;
import javax.swing.JTextField;
import javax.swing.JTree;
import javax.swing.KeyStroke;
import javax.swing.UIManager;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.TreeModel;
import bibliothek.gui.dock.common.CControl;
import bibliothek.gui.dock.common.CGrid;
import ca.odell.glazedlists.BasicEventList;
import ca.odell.glazedlists.EventList;
import ca.odell.glazedlists.FilterList;
import ca.odell.glazedlists.GlazedLists;
import ca.odell.glazedlists.SortedList;
import ca.odell.glazedlists.event.ListEvent;
import ca.odell.glazedlists.event.ListEventListener;
import ca.odell.glazedlists.swing.DefaultEventSelectionModel;
import ca.odell.glazedlists.swing.DefaultEventTableModel;
import ca.odell.glazedlists.swing.GlazedListsSwing;
import ca.odell.glazedlists.swing.TableComparatorChooser;
import ca.odell.glazedlists.swing.TextComponentMatcherEditor;
import de.schwarzrot.base.dock.BasicDockable;
import de.schwarzrot.base.table.PropertyTableFormat;
import de.schwarzrot.base.util.ActionManager;
import de.schwarzrot.base.util.ApplicationServiceProvider;
import de.schwarzrot.base.util.CallbackDefinition;
import de.schwarzrot.base.util.ImageFactory;
import de.schwarzrot.base.util.MessageBundle;
import de.schwarzrot.base.util.SuccessHandler;
import de.schwarzrot.control.client.CMPClient;
import de.schwarzrot.control.client.MediaClientExecutor;
import de.schwarzrot.control.config.ConfigDialog;
import de.schwarzrot.control.config.ConfigFactory;
import de.schwarzrot.control.dnd.ListSelectionSourceTransferHandler;
import de.schwarzrot.control.dnd.TreeSourceAndDestTransferHandler;
import de.schwarzrot.control.support.MediaTextFilterator;
import de.schwarzrot.control.support.MediaTypeSelector;
import de.schwarzrot.control.support.SelectedMedia;
import de.schwarzrot.control.support.TreeSelectionFilter;
import de.schwarzrot.control.table.MediaTableFormat;
import de.schwarzrot.media.domain.AbstractMediaNode;
import de.schwarzrot.media.domain.Config;
import de.schwarzrot.media.domain.Genre;
import de.schwarzrot.media.domain.Media;
import de.schwarzrot.media.domain.MediaServer;
import de.schwarzrot.media.domain.PlayerDefinition;
import de.schwarzrot.media.service.DataManager;
import de.schwarzrot.media.service.MediaExecutor;
import de.schwarzrot.media.util.ListLoader;
import de.schwarzrot.media.util.MedialistParser;
public class CMPCJ extends WindowAdapter implements PropertyChangeListener, SuccessHandler,
ListEventListener, MediaClientExecutor {
private static final String PREFFERRED_LOOK_N_FEEL = "Nimbus"; //$NON-NLS-1$
private static final String USER_HOME = "user.home"; //$NON-NLS-1$
private static final String DOCKING_CONFIG = "srdocking.conf"; //$NON-NLS-1$
enum Commands {
SrvOpen, SrvRefresh, SrvClose, SrvShutdown, TransmitChanges, Preferences, HelpHelp, HelpAbout
};
public CMPCJ(String[] args, ConfigFactory configFactory) {
nodeCache = new HashMap();
changes = new BasicEventList();
rootNode = new DefaultMutableTreeNode(setupServices(args, configFactory));
changes.addListEventListener(this);
}
public void createAndShow() {
@SuppressWarnings("unchecked")
ActionManager am = ApplicationServiceProvider.getService(ActionManager.class);
MessageBundle mb = ApplicationServiceProvider.getService(MessageBundle.class);
mediaList = new SortedList(GlazedListsSwing.swingThreadProxyList(new BasicEventList()));
try {
String lookNFeel = UIManager.getSystemLookAndFeelClassName();
for (UIManager.LookAndFeelInfo lfi : UIManager.getInstalledLookAndFeels()) {
if (PREFFERRED_LOOK_N_FEEL.equals(lfi.getName()))
lookNFeel = lfi.getClassName();
}
UIManager.setLookAndFeel(lookNFeel);
} catch (Exception e) {
System.err.println(mb.getMessage(CMPMessageBundle.MCC_6));
}
am.addCallbackDefinition(new CallbackDefinition(Commands.SrvOpen) {
private static final long serialVersionUID = 713L;
@Override
public void actionPerformed(ActionEvent e) {
doConnectServer();
}
});
am.addCallbackDefinition(new CallbackDefinition(Commands.SrvRefresh, false) {
private static final long serialVersionUID = 713L;
@Override
public void actionPerformed(ActionEvent e) {
doServerRescan();
}
});
am.addCallbackDefinition(new CallbackDefinition(Commands.SrvClose, false) {
private static final long serialVersionUID = 713L;
@Override
public void actionPerformed(ActionEvent e) {
doDisconnectServer();
}
});
am.addCallbackDefinition(new CallbackDefinition(Commands.SrvShutdown, false) {
private static final long serialVersionUID = 713L;
@Override
public void actionPerformed(ActionEvent e) {
doShutdownServer();
}
});
am.addCallbackDefinition(new CallbackDefinition(Commands.TransmitChanges, false) {
private static final long serialVersionUID = 713L;
@Override
public void actionPerformed(ActionEvent e) {
doTransmitChanges();
}
});
am.addCallbackDefinition(new CallbackDefinition(Commands.Preferences) {
private static final long serialVersionUID = 713L;
@Override
public void actionPerformed(ActionEvent e) {
doConfigureClient();
}
});
am.addCallbackDefinition(new CallbackDefinition(Commands.HelpHelp, false) {
private static final long serialVersionUID = 713L;
@Override
public void actionPerformed(ActionEvent e) {
doShowHelp();
}
});
am.addCallbackDefinition(new CallbackDefinition(Commands.HelpAbout) {
private static final long serialVersionUID = 713L;
@Override
public void actionPerformed(ActionEvent e) {
doShowAbout();
}
});
JMenuBar menuBar = new JMenuBar();
frame = new JFrame(mb.getMessage(CMPMessageBundle.MCC_7));
control = new CControl(frame);
frame.addWindowListener(this);
frame.add(setupPerspective(frame));
frame.setJMenuBar(menuBar);
frame.pack();
frame.setSize(1200, 800);
frame.setLocationRelativeTo(null);
restorePerspective();
createMenu(menuBar);
frame.setVisible(true);
}
@Override
public void handleFailure(Throwable t) {
// loading list from server failed ...
if (t instanceof UnknownHostException) {
//TODO: tell the user ...
}
t.printStackTrace();
tree.updateUI();
updateServerActions(false);
}
@Override
public void handleSuccess() {
// loading list from server was successful
tree.updateUI();
updateServerActions(true);
}
@Override
public void listChanged(ListEvent arg0) {
// get rid of changes made by user
@SuppressWarnings("unchecked")
ActionManager am = ApplicationServiceProvider.getService(ActionManager.class);
boolean changesExists = changes.size() > 0;
System.out.println("changes updated ...");
am.getAction(Commands.TransmitChanges).setEnabled(changesExists);
}
@Override
public void playMedia(Media m) {
Config config = ApplicationServiceProvider.getService(Config.class);
List command = new ArrayList();
PlayerDefinition pd = config.getPlayerMap().get(m.getType());
StringBuilder sb = new StringBuilder("http://");
sb.append(clientStub.getServerDefinition().getHostName());
sb.append(":");
sb.append(clientStub.getServerDefinition().getPort());
sb.append(m.getURI());
command.add(pd.getExecutable());
for (String param : pd.getParameters()) {
command.add(param);
}
command.add(sb.toString());
ProcessBuilder pb = new ProcessBuilder(command);
pb.redirectErrorStream(true);
try {
Process proc = pb.start();
BufferedReader br = new BufferedReader(new InputStreamReader(proc.getInputStream()));
String line;
if (config.isDebug()) {
while ((line = br.readLine()) != null) {
System.err.println(line);
}
} else {
while ((line = br.readLine()) != null)
;
}
} catch (Throwable t) {
t.printStackTrace();
}
}
@Override
public void propertyChange(PropertyChangeEvent evt) {
propertyList.getReadWriteLock().writeLock().lock();
propertyList.clear();
if (selectedMedia.getMedia() != null)
propertyList.addAll(selectedMedia.getMedia().getProperties().entrySet());
propertyList.getReadWriteLock().writeLock().unlock();
}
@Override
public void windowClosing(WindowEvent e) {
savePerspective();
control.destroy();
System.exit(0);
}
@Override
public void windowOpened(WindowEvent e) {
doConnectServer();
}
protected JComponent createMediaTableView() {
MessageBundle mb = ApplicationServiceProvider.getService(MessageBundle.class);
JTextField filterEdit = new JTextField(30);
mediaTypeSelector = new MediaTypeSelector(mediaList);
FilterList typeFilteredMedias = new FilterList(mediaList, mediaTypeSelector);
FilterList filteredMedias = new FilterList(typeFilteredMedias, new TreeSelectionFilter(tree));
FilterList textFilteredMedias = new FilterList(filteredMedias,
new TextComponentMatcherEditor(filterEdit, new MediaTextFilterator()));
MediaTableFormat mtf = new MediaTableFormat();
DefaultEventTableModel mediaTableModel = new DefaultEventTableModel(textFilteredMedias, mtf);
selectedMedia = new SelectedMedia(textFilteredMedias);
DefaultEventSelectionModel selectionModel = new DefaultEventSelectionModel(textFilteredMedias);
JTable mediaJTable = new JTable(mediaTableModel);
selectedMedia.addPropertyChangeListener(this);
selectionModel.addListSelectionListener(selectedMedia);
mediaJTable.setDragEnabled(true);
mediaJTable.setTransferHandler(new ListSelectionSourceTransferHandler(selectionModel));
mediaJTable.setSelectionModel(selectionModel);
mediaJTable.addMouseListener(new MediaExecutor(mediaJTable, textFilteredMedias, this));
@SuppressWarnings({ "rawtypes", "unused" })
TableComparatorChooser tableSorter = TableComparatorChooser.install(mediaJTable, mediaList,
TableComparatorChooser.MULTIPLE_COLUMN_MOUSE_WITH_UNDO);
JScrollPane mediaTableScrollPane = new JScrollPane(mediaJTable);
int mx = mediaJTable.getColumnModel().getColumnCount();
for (int i = 0; i < mx; ++i) {
int cw = mtf.getColumnWidth(i);
if (cw > 0)
mediaJTable.getColumnModel().getColumn(i).setMaxWidth(cw);
}
JPanel tablePane = new JPanel();
JLabel lFilter = new JLabel(mb.getMessage(CMPMessageBundle.MCC_18));
tablePane.setLayout(new GridBagLayout());
tablePane.add(lFilter, new GridBagConstraints(0, 0, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER,
GridBagConstraints.BOTH, new Insets(0, 0, 0, 0), 0, 0));
tablePane.add(filterEdit, new GridBagConstraints(1, 0, 1, 1, 1.0, 0.0, GridBagConstraints.CENTER,
GridBagConstraints.BOTH, new Insets(0, 0, 0, 0), 0, 0));
tablePane.add(mediaTableScrollPane, new GridBagConstraints(0, 1, 2, 1, 1.0, 1.0, GridBagConstraints.CENTER,
GridBagConstraints.BOTH, new Insets(0, 0, 0, 0), 0, 0));
return tablePane;
}
protected JComponent createMediaTypeList() {
JPanel rv = new JPanel();
return rv;
}
protected void createMenu(JMenuBar menuBar) {
MessageBundle mb = ApplicationServiceProvider.getService(MessageBundle.class);
@SuppressWarnings("unchecked")
ActionManager am = ApplicationServiceProvider.getService(ActionManager.class);
JMenu mServer = new JMenu(mb.getMessage(CMPMessageBundle.MCC_19));
JMenu mEdit = new JMenu(mb.getMessage(CMPMessageBundle.MCC_20));
JMenu mWindow = new JMenu(mb.getMessage(CMPMessageBundle.MCC_21));
JMenu mHelp = new JMenu(mb.getMessage(CMPMessageBundle.MCC_22));
mServer.add(new JMenuItem(am.getAction(Commands.SrvOpen)));
mServer.add(new JMenuItem(am.getAction(Commands.SrvRefresh)));
mServer.add(new JMenuItem(am.getAction(Commands.SrvClose)));
mServer.add(new JSeparator());
mServer.add(new JMenuItem(am.getAction(Commands.SrvShutdown)));
mEdit.add(new JMenuItem(am.getAction(Commands.TransmitChanges)));
mEdit.add(new JSeparator());
mEdit.add(new JMenuItem(am.getAction(Commands.Preferences)));
mWindow.add(cdGenreTree.createMenuItem());
mWindow.add(cdMediaTypes.createMenuItem());
mWindow.add(cdProperties.createMenuItem());
//TODO: not yet
// mHelp.add(new JMenuItem(getAction(Commands.HelpHelp)));
// mHelp.add(new JSeparator());
mHelp.add(new JMenuItem(am.getAction(Commands.HelpAbout)));
menuBar.add(mServer);
menuBar.add(mEdit);
menuBar.add(mWindow);
menuBar.add(mHelp);
}
protected JComponent createPropertyView() {
if (selectedMedia.getMedia() != null)
propertyList = GlazedLists.eventList(selectedMedia.getMedia().getProperties().entrySet());
else
propertyList = new BasicEventList>();
SortedList> psl = new SortedList>(propertyList,
new Comparator>() {
@Override
public int compare(Entry a, Entry b) {
return a.getKey().compareToIgnoreCase(b.getKey());
}
});
PropertyTableFormat ptf = new PropertyTableFormat();
DefaultEventTableModel> tm = new DefaultEventTableModel>(
psl, ptf);
JTable propertyTable = new JTable(tm);
JScrollPane psp = new JScrollPane(propertyTable);
int mx = propertyTable.getColumnModel().getColumnCount();
for (int i = 0; i < mx; ++i) {
int cw = ptf.getColumnWidth(i);
if (cw > 0)
propertyTable.getColumnModel().getColumn(i).setMaxWidth(cw);
}
return psp;
}
protected JComponent createTreeView() {
TreeModel tm = new DefaultTreeModel(rootNode);
tree = new JTree(tm);
tree.setDragEnabled(true);
tree.setEditable(true);
tree.getActionMap().put("removeGenre", new AbstractAction() { //$NON-NLS-1$
private static final long serialVersionUID = 713L;
@Override
public void actionPerformed(ActionEvent e) {
doRemoveGenre();
}
});
tree.getActionMap().put("createGenre", new AbstractAction() { //$NON-NLS-1$
private static final long serialVersionUID = 713L;
@Override
public void actionPerformed(ActionEvent e) {
doCreateGenre();
}
});
tree.getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_DELETE, 0), "removeGenre"); //$NON-NLS-1$
tree.getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_INSERT, 0), "createGenre"); //$NON-NLS-1$
tree.setDropMode(DropMode.USE_SELECTION);
tree.setTransferHandler(new TreeSourceAndDestTransferHandler(tree, nodeCache, changes));
JScrollPane treeView = new JScrollPane(tree);
return treeView;
}
protected void doConfigureClient() {
ConfigDialog cd = new ConfigDialog(frame);
cd.showDialog(frame);
}
protected void doConnectServer() {
MessageBundle mb = ApplicationServiceProvider.getService(MessageBundle.class);
Config config = ApplicationServiceProvider.getService(Config.class);
Object ms = JOptionPane.showInputDialog(frame, mb.getMessage(CMPMessageBundle.MCC_12), mb
.getMessage(CMPMessageBundle.MCC_13), JOptionPane.PLAIN_MESSAGE, null, config.getKnownServers()
.toArray(), mb.getMessage(CMPMessageBundle.MCC_14));
if (ms instanceof MediaServer) {
System.out.println(mb.getMessage(CMPMessageBundle.MCC_15) + ((MediaServer) ms).getHostName()
+ mb.getMessage(CMPMessageBundle.MCC_16) + ((MediaServer) ms).getPort());
clientStub = new CMPClient((MediaServer) ms);
ApplicationServiceProvider.registerService(ListLoader.class, clientStub);
DataManager dm = ApplicationServiceProvider.getService(DataManager.class);
dm.load(rootNode, mediaList, this);
}
}
// respond to tree action
protected void doCreateGenre() {
MessageBundle mb = ApplicationServiceProvider.getService(MessageBundle.class);
System.out.println(mb.getMessage(CMPMessageBundle.MCC_17));
//TODO: ask user for genre name and create node and cache entries
tree.updateUI();
};
protected void doDisconnectServer() {
ApplicationServiceProvider.registerService(MediaServer.class, null);
mediaList.getReadWriteLock().writeLock().lock();
mediaList.clear();
mediaList.getReadWriteLock().writeLock().unlock();
((DefaultMutableTreeNode) tree.getModel().getRoot()).removeAllChildren();
tree.updateUI();
selectedMedia.clear();
updateServerActions(false);
clientStub = null;
}
// respond to tree action
protected void doRemoveGenre() {
MessageBundle mb = ApplicationServiceProvider.getService(MessageBundle.class);
System.out.println(mb.getMessage(CMPMessageBundle.MCC_0));
DefaultMutableTreeNode node = (DefaultMutableTreeNode) tree.getSelectionPath().getLastPathComponent();
if (node == null)
return;
Genre g = (Genre) node.getUserObject();
if (g.getMediaList().size() > 0 || g.getChildren().size() > 0) {
//TODO: ask user and/or pop some warning
} else {
nodeCache.remove(g.getRealPath());
((DefaultMutableTreeNode) node.getParent()).remove(node);
tree.updateUI();
}
}
protected void doServerRescan() {
clientStub.execServerRescan();
}
protected void doShowAbout() {
AboutDialog dlgAbout = new AboutDialog(frame);
dlgAbout.showDialog(frame);
}
protected void doShowHelp() {
}
protected void doShutdownServer() {
clientStub.execServerShutdown();
doDisconnectServer();
}
protected void doTransmitChanges() {
clientStub.transmitChanges(changes);
}
protected void restoreAppStatus() {
Config config = ApplicationServiceProvider.getService(Config.class);
try {
if (config != null) {
if (config.getMainWidth() > 0 && config.getMainHeight() > 0)
frame.setSize(config.getMainWidth(), config.getMainHeight());
if (config.getPosX() > 0 || config.getPosY() > 0)
frame.setLocation(config.getPosX(), config.getPosY());
}
} catch (Throwable t) {
t.printStackTrace();
}
}
protected void restorePerspective() {
File dockableSettings = new File(configDirectory, DOCKING_CONFIG);
try {
if (dockableSettings.exists() && dockableSettings.canRead())
control.getResources().readFile(dockableSettings);
} catch (Throwable t) {
t.printStackTrace();
}
restoreAppStatus();
}
protected void saveAppStatus() {
Config config = ApplicationServiceProvider.getService(Config.class);
ConfigFactory cf = ApplicationServiceProvider.getService(ConfigFactory.class);
try {
Dimension mainDim = frame.getSize();
Point mainPos = frame.getLocation();
config.setMainWidth(mainDim.width);
config.setMainHeight(mainDim.height);
config.setPosX(mainPos.x);
config.setPosY(mainPos.y);
cf.putConfig(config);
} catch (Throwable t) {
t.printStackTrace();
}
}
protected void savePerspective() {
File dockableSettings = new File(configDirectory, DOCKING_CONFIG);
try {
if (!dockableSettings.exists())
dockableSettings.getParentFile().mkdirs();
control.getResources().writeFile(dockableSettings);
} catch (Throwable t) {
t.printStackTrace();
}
saveAppStatus();
}
protected JComponent setupPerspective(JFrame frame) {
MessageBundle mb = ApplicationServiceProvider.getService(MessageBundle.class);
cdGenreTree = new BasicDockable("genreTree", mb.getMessage(CMPMessageBundle.MCC_29), createTreeView()); //$NON-NLS-1$
cdMediaList = new BasicDockable("mediaList", mb.getMessage(CMPMessageBundle.MCC_31), createMediaTableView()); //$NON-NLS-1$
cdMediaTypes = new BasicDockable(
"mediaTypes", mb.getMessage(CMPMessageBundle.MCC_33), mediaTypeSelector.getComponent()); //$NON-NLS-1$
cdProperties = new BasicDockable("properties", mb.getMessage(CMPMessageBundle.MCC_35), createPropertyView()); //$NON-NLS-1$
CGrid grid = new CGrid(control);
grid.add(0, 0, 1, 2, cdGenreTree);
grid.add(1, 0, 1, 1, cdMediaList);
grid.add(2, 0, 1, 2, cdMediaTypes);
grid.add(1, 1, 1, 1, cdProperties);
cdMediaList.setStackable(false);
cdMediaList.setExternalizable(false);
cdMediaList.setMinimizable(false);
control.getContentArea().deploy(grid);
return control.getContentArea();
}
protected Genre setupServices(String[] args, ConfigFactory configFactory) {
MessageBundle mb = new CMPMessageBundle();
configDirectory = configFactory.getConfigDirectory();
ApplicationServiceProvider.registerService(Config.class, configFactory.getConfig());
ApplicationServiceProvider.registerService(ConfigFactory.class, configFactory);
ApplicationServiceProvider.registerService(MessageBundle.class, mb);
if (args.length > 0) {
File tmp = new File(args[0]);
if (tmp.exists() && tmp.canRead()) {
System.out.println(mb.getMessage(CMPMessageBundle.MCC_4) + " " + tmp.getAbsolutePath()
+ mb.getMessage(CMPMessageBundle.MCC_5));
input = tmp;
}
}
ApplicationServiceProvider.registerService(ActionManager.class, new ActionManager());
ApplicationServiceProvider.registerService(DataManager.class, new DataManager(input));
ApplicationServiceProvider.registerService(ImageFactory.class, new ImageFactory());
ApplicationServiceProvider.registerService(MedialistParser.class, new MedialistParser());
File base = new File("/"); //$NON-NLS-1$
Genre root = new Genre(base);
// ApplicationServiceProvider.registerService(FilesystemListLoader.class, new FilesystemListLoader(base));
return root;
}
protected void updateServerActions(boolean connected) {
@SuppressWarnings("unchecked")
ActionManager am = ApplicationServiceProvider.getService(ActionManager.class);
am.getAction(Commands.SrvOpen).setEnabled(!connected);
am.getAction(Commands.SrvClose).setEnabled(connected);
am.getAction(Commands.SrvRefresh).setEnabled(connected);
am.getAction(Commands.SrvShutdown).setEnabled(connected);
}
public static void main(final String[] args) {
javax.swing.SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
File configDir = new File(System.getProperty(USER_HOME), ".cmp"); //$NON-NLS-1$
CMPCJ app = new CMPCJ(args, new ConfigFactory(configDir, "CMPCJ"));
app.createAndShow();
}
});
}
private DefaultMutableTreeNode rootNode;
private SortedList mediaList;
private SelectedMedia selectedMedia;
private EventList changes;
private Map nodeCache;
private EventList> propertyList;
private MediaTypeSelector mediaTypeSelector;
private CMPClient clientStub;
private CControl control;
private JFrame frame;
private BasicDockable cdMediaList;
private BasicDockable cdGenreTree;
private BasicDockable cdMediaTypes;
private BasicDockable cdProperties;
private File configDirectory;
private JTree tree;
private File input;
}