/*******************************************************************************
* Australian National University Data Commons
* Copyright (C) 2013 The Australian National University
*
* This file is part of Australian National University Data Commons.
*
* Australian National University Data Commons 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 au.edu.anu.dcclient.gui;
import static java.text.MessageFormat.format;
import gov.loc.repository.bagit.Manifest.Algorithm;
import java.awt.BorderLayout;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.Transferable;
import java.awt.datatransfer.UnsupportedFlavorException;
import java.awt.dnd.DnDConstants;
import java.awt.dnd.DropTarget;
import java.awt.dnd.DropTargetDragEvent;
import java.awt.dnd.DropTargetDropEvent;
import java.awt.dnd.DropTargetEvent;
import java.awt.dnd.DropTargetListener;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
import java.beans.PropertyChangeEvent;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.ExecutionException;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JProgressBar;
import javax.swing.JScrollPane;
import javax.swing.JTabbedPane;
import javax.swing.JTextField;
import javax.swing.JToolBar;
import javax.swing.SwingUtilities;
import javax.swing.SwingWorker;
import javax.swing.TransferHandler;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import javax.swing.event.TreeSelectionEvent;
import javax.swing.event.TreeSelectionListener;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.TreePath;
import net.miginfocom.swing.MigLayout;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import au.edu.anu.datacommons.storage.info.BagSummary;
import au.edu.anu.datacommons.storage.info.FileSummary;
import au.edu.anu.dcclient.Global;
import au.edu.anu.dcclient.bagfilesexplorer.BagFilesTree;
import au.edu.anu.dcclient.tasks.DownloadFilesTask;
import au.edu.anu.dcclient.tasks.GetBagSummaryTask;
import au.edu.anu.dcclient.tasks.UploadFilesetTask;
/**
* This class represents the main application window.
*/
public class MainWindow extends JFrame implements ActionListener
{
private static final long serialVersionUID = 1L;
private static final Logger LOGGER = LoggerFactory.getLogger(MainWindow.class);
private static MainWindow instance = null;
private JPanel panel_north;
private JPanel panel_west;
private JPanel panel_east;
private JPanel panel_bottom;
private JPanel panel_centre;
private JList listItems;
private JTextField txtPid;
private JButton btnGet;
private JLabel lblStatus;
private JProgressBar progressBar;
private JMenuBar menuBar;
private JMenu mnFile;
private JMenuItem mntmExit;
private JTextField txtSearch;
private JScrollPane scrollPane;
private JButton btnLogin;
private JTabbedPane tabbedPane;
private JLabel lblSearch;
private JLabel lblPid;
private JPanel pnlServerBag;
private JScrollPane scrollPane_1;
private BagFilesTree tree;
private final JToolBar toolBar = new JToolBar();
private final JPanel panel = new JPanel();
private final JLabel lblFileSize = new JLabel("File Size");
private final JLabel lblFileSizeValue = new JLabel("");
private final JLabel lblMd5 = new JLabel("MD5");
private final JLabel lblMd5Value = new JLabel("");
/**
* Create the application.
*/
public MainWindow()
{
setTitle("ANU Data Commons");
initGui();
lblStatus.setText(Global.getAppServerUriAsString());
instance = this;
}
/**
* Initialize the contents of the frame.
*/
private void initGui()
{
BorderLayout borderLayout = (BorderLayout) this.getContentPane().getLayout();
this.setBounds(100, 100, 688, 586);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.panel_north = new JPanel();
this.getContentPane().add(this.panel_north, BorderLayout.NORTH);
this.btnLogin = new JButton("Login");
this.btnLogin.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
LoginDialog ld = new LoginDialog(MainWindow.this);
if (ld.display() == JOptionPane.OK_OPTION) {
String[] userInfo = ld.getUserInfo();
if (userInfo != null) {
setControlsEnabled(true);
btnLogin.setText(format("Switch user ({0}, {1})", userInfo[0], userInfo[1]));
MainWindow.this.getRootPane().setDefaultButton(btnGet);
txtPid.requestFocusInWindow();
} else {
setControlsEnabled(false);
btnLogin.setText("Login");
MainWindow.this.getRootPane().setDefaultButton(btnLogin);
JOptionPane.showMessageDialog(MainWindow.this, "Unable to log into ANU Data Commons",
"Authentication Error", JOptionPane.ERROR_MESSAGE);
}
}
}
});
}
});
panel_north.setLayout(new MigLayout("", "[87px,grow][57px]", "[23px]"));
panel_north.add(toolBar, "cell 0 0,alignx left,aligny center");
this.panel_north.add(this.btnLogin, "cell 1 0,alignx left,aligny top");
this.menuBar = new JMenuBar();
this.setJMenuBar(this.menuBar);
this.mnFile = new JMenu("File");
this.menuBar.add(this.mnFile);
this.mntmExit = new JMenuItem("Exit");
this.mntmExit.addActionListener(this);
this.mnFile.add(this.mntmExit);
this.panel_west = new JPanel();
this.getContentPane().add(this.panel_west, BorderLayout.WEST);
this.panel_east = new JPanel();
this.getContentPane().add(this.panel_east, BorderLayout.EAST);
this.panel_bottom = new JPanel();
this.getContentPane().add(this.panel_bottom, BorderLayout.SOUTH);
panel_bottom.setLayout(new MigLayout("", "[184px,grow][440px,grow]", "[19px]"));
this.lblStatus = new JLabel("Status");
this.panel_bottom.add(this.lblStatus, "cell 0 0,alignx left,aligny center");
this.progressBar = new JProgressBar();
progressBar.setStringPainted(true);
this.panel_bottom.add(this.progressBar, "cell 1 0,growx,aligny center");
this.panel_centre = new JPanel();
this.getContentPane().add(this.panel_centre, BorderLayout.CENTER);
panel_centre.setLayout(new MigLayout("", "[33px][27px][86px][111px][66.00px,grow][160px,grow]", "[20px][408px,grow][23px]"));
this.lblSearch = new JLabel("Search");
this.panel_centre.add(this.lblSearch, "cell 0 0,alignx left,aligny center");
this.txtSearch = new JTextField();
this.panel_centre.add(this.txtSearch, "cell 2 0,growx,aligny center");
this.txtSearch.setColumns(10);
this.scrollPane = new JScrollPane();
this.panel_centre.add(this.scrollPane, "cell 0 1 3 1,grow");
this.listItems = new JList();
this.scrollPane.setViewportView(this.listItems);
this.tabbedPane = new JTabbedPane(JTabbedPane.TOP);
this.panel_centre.add(this.tabbedPane, "cell 3 1 2 1,grow");
panel_centre.add(panel, "cell 5 1,grow");
panel.setLayout(new MigLayout("", "[][grow]", "[][]"));
panel.add(lblFileSize, "cell 0 0");
panel.add(lblFileSizeValue, "cell 1 0");
panel.add(lblMd5, "cell 0 1");
panel.add(lblMd5Value, "cell 1 1");
this.lblPid = new JLabel("Pid");
this.lblPid.setDisplayedMnemonic('P');
this.panel_centre.add(this.lblPid, "cell 0 2,alignx left,aligny center");
this.txtPid = new JTextField();
this.lblPid.setLabelFor(this.txtPid);
this.txtPid.setEnabled(false);
this.panel_centre.add(this.txtPid, "cell 2 2,growx,aligny center");
this.txtPid.setColumns(10);
this.txtPid.addFocusListener(new FocusListener() {
@Override
public void focusGained(final FocusEvent e) {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
((JTextField) e.getComponent()).selectAll();
}
});
}
@Override
public void focusLost(FocusEvent e) {
// TODO Auto-generated method stub
}
});
this.txtPid.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
JButton defaultButton = MainWindow.this.getRootPane().getDefaultButton();
if (defaultButton.isEnabled()) {
defaultButton.doClick();
}
}
});
this.txtPid.getDocument().addDocumentListener(new DocumentListener()
{
@Override
public void insertUpdate(DocumentEvent e)
{
toggleRetrieve(e);
}
@Override
public void removeUpdate(DocumentEvent e)
{
toggleRetrieve(e);
}
@Override
public void changedUpdate(DocumentEvent e)
{
// TODO Determine when this event is fired, if at all.
}
private void toggleRetrieve(DocumentEvent e)
{
if (txtPid.getText().length() > 0)
btnGet.setEnabled(true);
else
btnGet.setEnabled(false);
}
});
this.btnGet = new JButton("Retrieve File List");
this.btnGet.setEnabled(false);
this.btnGet.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
GetBagSummaryTask getBagSummaryTask = new GetBagSummaryTask(txtPid.getText().toLowerCase().trim()) {
@Override
protected void done() {
super.done();
try {
BagSummary bagSummary = get();
BagFilesTree tree = new BagFilesTree(bagSummary);
tree.addPropertyChangeListener(new TaskProgressListener(progressBar) {
@Override
public void propertyChange(PropertyChangeEvent evt) {
super.propertyChange(evt);
if ("state".equals(evt.getPropertyName()) && evt.getNewValue().equals(SwingWorker.StateValue.DONE)) {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
btnGet.doClick();
}
});
}
}
});
tree.setTransferHandler(new TransferHandler() {
private static final long serialVersionUID = 1L;
@Override
public int getSourceActions(JComponent c)
{
return COPY;
}
@Override
public boolean canImport(TransferSupport support) {
// TODO Implement
return super.canImport(support);
}
@Override
protected Transferable createTransferable(JComponent component)
{
LOGGER.trace("In createTransferable");
TreePath[] paths = ((BagFilesTree) component).getSelectionPaths();
List<String> filepaths = new ArrayList<String>();
for (int i = 0; i < paths.length; i++) {
DefaultMutableTreeNode node = (DefaultMutableTreeNode) (paths[i].getLastPathComponent());
Entry<String, FileSummary> entry = (Entry<String, FileSummary>) node.getUserObject();
filepaths.add(entry.getKey());
}
DownloadFilesTask dlTask = new DownloadFilesTask(txtPid.getText(), filepaths);
dlTask.addPropertyChangeListener(new TaskProgressListener(progressBar));
Transferable transferable = new GenericTransferable(dlTask);
return transferable;
}
@Override
protected void exportDone(JComponent source, Transferable data, int action) {
super.exportDone(source, data, action);
LOGGER.trace("In exportDone");
}
class GenericTransferable implements Transferable
{
private final DownloadFilesTask dlTask;
public GenericTransferable(DownloadFilesTask dlTask) {
this.dlTask = dlTask;
}
@Override
public DataFlavor[] getTransferDataFlavors()
{
return new DataFlavor[] { DataFlavor.javaFileListFlavor };
}
@Override
public boolean isDataFlavorSupported(DataFlavor flavor)
{
return flavor.equals(DataFlavor.javaFileListFlavor);
}
@Override
public Object getTransferData(DataFlavor flavor) throws UnsupportedFlavorException, IOException
{
LOGGER.trace("In getTransferData {} {}", flavor.toString(), this.toString());
Map<String, File> fileResult;
List<File> dlFiles = new ArrayList<File>();
if (flavor.equals(DataFlavor.javaFileListFlavor)) {
if (dlTask.getState() == SwingWorker.StateValue.PENDING) {
dlTask.execute();
}
if (dlTask.getState() == SwingWorker.StateValue.DONE) {
try {
fileResult = dlTask.get();
dlFiles.addAll(fileResult.values());
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (ExecutionException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
} else {
throw new UnsupportedFlavorException(flavor);
}
return dlFiles;
}
}
});
tree.addTreeSelectionListener(new TreeSelectionListener () {
@Override
public void valueChanged(TreeSelectionEvent e) {
TreePath newLeadSelectionPath = e.getNewLeadSelectionPath();
if (newLeadSelectionPath != null) {
DefaultMutableTreeNode selectedNode = (DefaultMutableTreeNode) newLeadSelectionPath
.getLastPathComponent();
Entry<String, FileSummary> entry = (Entry<String, FileSummary>) selectedNode
.getUserObject();
lblFileSizeValue.setText(entry.getValue().getFriendlySize());
lblMd5Value.setText(entry.getValue().getMessageDigests().get(Algorithm.MD5.javaSecurityAlgorithm));
} else {
lblFileSizeValue.setText("");
lblMd5Value.setText("");
}
}
});
tree.setDropTarget(new DropTarget(tree, new DropTargetListener() {
@Override
public void dragEnter(DropTargetDragEvent dtde) {
// TODO Auto-generated method stub
}
@Override
public void dragOver(DropTargetDragEvent dtde) {
// TODO Auto-generated method stub
}
@Override
public void dropActionChanged(DropTargetDragEvent dtde) {
// TODO Auto-generated method stub
}
@Override
public void dragExit(DropTargetEvent dte) {
// TODO Auto-generated method stub
}
@Override
public void drop(DropTargetDropEvent dtde) {
try {
Transferable tr = dtde.getTransferable();
DataFlavor[] flavors = tr.getTransferDataFlavors();
for (int i = 0; i < flavors.length; i++) {
if (flavors[i].isFlavorJavaFileListType()) {
dtde.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE);
List<File> files = (List<File>) tr.getTransferData(flavors[i]);
BagFilesTree bagFilesTree = (BagFilesTree) ((DropTarget) dtde.getSource()).getComponent();
String pid = bagFilesTree.getBagSummary().getPid();
UploadFilesetTask ulFilesetTask = new UploadFilesetTask(pid, files) {
@Override
protected void done() {
super.done();
btnGet.doClick();
}
};
ulFilesetTask.addPropertyChangeListener(new TaskProgressListener(progressBar));
ulFilesetTask.execute();
dtde.dropComplete(true);
}
}
} catch (Exception e) {
e.printStackTrace();
dtde.rejectDrop();
}
return;
}
}));
scrollPane_1.setViewportView(tree);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (ExecutionException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
};
getBagSummaryTask.addPropertyChangeListener(new TaskProgressListener(progressBar));
getBagSummaryTask.execute();
}
});
this.pnlServerBag = new JPanel();
this.tabbedPane.addTab("Server Bag", null, this.pnlServerBag, null);
GridBagLayout gbl_pnlServerBag = new GridBagLayout();
gbl_pnlServerBag.columnWidths = new int[]{0, 0};
gbl_pnlServerBag.rowHeights = new int[]{0, 0};
gbl_pnlServerBag.columnWeights = new double[]{1.0, Double.MIN_VALUE};
gbl_pnlServerBag.rowWeights = new double[]{1.0, Double.MIN_VALUE};
pnlServerBag.setLayout(gbl_pnlServerBag);
scrollPane_1 = new JScrollPane();
GridBagConstraints gbc_scrollPane_1 = new GridBagConstraints();
gbc_scrollPane_1.fill = GridBagConstraints.BOTH;
gbc_scrollPane_1.gridx = 0;
gbc_scrollPane_1.gridy = 0;
pnlServerBag.add(scrollPane_1, gbc_scrollPane_1);
this.panel_centre.add(this.btnGet, "cell 3 2,alignx center,aligny center");
}
@Override
public void actionPerformed(ActionEvent e)
{
if (e.getSource() == this.btnLogin)
{
do_btnLogin_actionPerformed(e);
}
if (e.getSource() == this.mntmExit)
{
do_mntmExit_actionPerformed(e);
}
}
/**
* Method called when the file menu item exit is clicked.
*
* @param e
* ActionEvent object
*/
protected void do_mntmExit_actionPerformed(ActionEvent e)
{
System.exit(0);
}
/**
* Method called when the Login button is clicked.
*
* @param e
* ActionEvent object
*/
protected void do_btnLogin_actionPerformed(ActionEvent e)
{
// TODO Remove this method.
}
/**
* Enables the disabled controls on this window.
*/
private void setControlsEnabled(boolean newState)
{
txtPid.setEnabled(newState);
}
/**
* Gets the singleton instance of this window.
*
* @return MainWindow instance
*/
public static MainWindow getInstance()
{
return instance;
}
}