package org.fnppl.opensdx.file_transfer.gui;
/*
* Copyright (C) 2010-2015
* fine people e.V. <opensdx@fnppl.org>
* Henning Thieß <ht@fnppl.org>
*
* http://fnppl.org
*/
/*
* Software license
*
* As far as this file or parts of this file is/are software, rather than documentation, this software-license applies / shall be applied.
*
* This file is part of openSDX
* openSDX is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* openSDX 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 Lesser General Public License
* and GNU General Public License along with openSDX.
* If not, see <http://www.gnu.org/licenses/>.
*
*/
/*
* Documentation license
*
* As far as this file or parts of this file is/are documentation, rather than software, this documentation-license applies / shall be applied.
*
* This file is part of openSDX.
* Permission is granted to copy, distribute and/or modify this document
* under the terms of the GNU Free Documentation License, Version 1.3
* or any later version published by the Free Software Foundation;
* with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts.
* A copy of the license is included in the section entitled "GNU
* Free Documentation License" resp. in the file called "FDL.txt".
*
*/
import java.awt.BorderLayout;
import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.Vector;
import javax.swing.JButton;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JSplitPane;
import javax.swing.JTable;
import javax.swing.JTree;
import javax.swing.RowSorter;
import javax.swing.RowSorter.SortKey;
import javax.swing.event.TreeExpansionEvent;
import javax.swing.event.TreeExpansionListener;
import javax.swing.event.TreeSelectionEvent;
import javax.swing.event.TreeSelectionListener;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableCellRenderer;
import javax.swing.table.TableColumnModel;
import javax.swing.table.TableModel;
import javax.swing.table.TableRowSorter;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.TreePath;
import org.fnppl.opensdx.file_transfer.CommandResponseListener;
import org.fnppl.opensdx.file_transfer.OSDXFileTransferClient;
import org.fnppl.opensdx.file_transfer.commands.OSDXFileTransferCommand;
import org.fnppl.opensdx.file_transfer.commands.OSDXFileTransferListCommand;
import org.fnppl.opensdx.file_transfer.model.RemoteFile;
import org.fnppl.opensdx.gui.Dialogs;
import org.fnppl.opensdx.gui.helper.MyObservable;
import org.fnppl.opensdx.gui.helper.MyObserver;
public class TreeAndTablePanelOSDXClient extends JPanel implements MyObservable, TreeAndTableChildrenGetter {
private long timeoutDuration = 4000;
private JSplitPane split;
private JTree tree;
private TreeAndTableNode root;
private DefaultTreeModel tree_model;
private JTable table;
private DefaultTableModel table_model;
private Vector<RemoteFile> currentFiles = null;
private JPanel buttons;
private JButton buTransfer;
private JButton buMkdir;
private JButton buRemove;
private JButton buRename;
private JButton buRefresh;
private Comparator<TreeAndTableNode> compareNodes;
private OSDXFileTransferClient client = null;
public TreeAndTablePanelOSDXClient(OSDXFileTransferClient client) {
this.client = client;
compareNodes = new Comparator<TreeAndTableNode>() {
public int compare(TreeAndTableNode n1, TreeAndTableNode n2) {
return n1.toString().compareTo(n2.toString());
}
};
initComponents();
initLayout();
}
public void closeConnection() {
client.closeConnection();
}
private Vector<RemoteFile> nextList = null;
private boolean hasAnswer = false;
private Vector<RemoteFile> list(String absolutPath) {
nextList = null;
hasAnswer = false;
System.out.println("list :: "+absolutPath);
client.list(absolutPath,new CommandResponseListener() {
public void onError(OSDXFileTransferCommand command, String msg) {
//System.out.println("END OF LIST COMMAND :: ERROR");
nextList = null;
hasAnswer = true;
}
public void onStatusUpdate(OSDXFileTransferCommand command,long progress, long maxProgress, String msg) {}
public void onSuccess(OSDXFileTransferCommand command) {
//System.out.println("END OF LIST COMMAND :: SUCCESS");
nextList = ((OSDXFileTransferListCommand)command).getList();
hasAnswer = true;
}
});
//block until answer or timeout
long timeout = System.currentTimeMillis()+timeoutDuration;
while (!hasAnswer && timeout > System.currentTimeMillis()) {
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
if (!hasAnswer) {
Dialogs.showMessage("Error: Timeout when requesting list for directory: "+absolutPath);
}
return nextList;
}
private Object objSync = new Object();
public Vector<TreeAndTableNode> getChildren(TreeAndTableNode node) {
RemoteFile file = (RemoteFile) node.getUserObject();
System.out.println("getChildren :: "+file.getFilnameWithPath());
try {
Vector<RemoteFile> list = list(file.getFilnameWithPath());
currentFiles = new Vector<RemoteFile>();
if (list!=null) {
Vector<TreeAndTableNode> children = new Vector<TreeAndTableNode>();
//build list
for (RemoteFile f : list) {
String name = f.getName();
try {
if (f.isDirectory()) {
System.out.println("adding directory: "+f.getPath()+"/"+name);
TreeAndTableNode n = new TreeAndTableNode(this, name, true, f);
children.add(n);
} else {
currentFiles.add(f);
}
} catch (Exception ex) {
ex.printStackTrace();
}
}
//sort
Collections.sort(children, compareNodes);
return children;
}
} catch (Exception ex) {
ex.printStackTrace();
}
return null;
}
public void setPreferredColumnWidth(int colNo, int width) {
try {
table.getColumnModel().getColumn(colNo).setPreferredWidth(width);
} catch (Exception ex) {}
}
public void setColumnRenderer(int colNo, TableCellRenderer renderer) {
try {
table.getColumnModel().getColumn(colNo).setCellRenderer(renderer);
} catch (Exception ex) {}
}
private void initComponents() {
tree = new JTree();
RemoteFile f = client.getRoot();
root = new TreeAndTableNode(this, f.getName(), true, f);
root.populate();
tree_model = new DefaultTreeModel(root);
tree.setModel(tree_model);
TreeExpansionListener expListen = new TreeExpansionListener() {
public void treeExpanded(TreeExpansionEvent event) {
TreePath path = event.getPath();
tree.setSelectionPath(path);
TreeAndTableNode node = (TreeAndTableNode)path.getLastPathComponent();
if (node.populate()) {
tree_model.nodeStructureChanged(node);
}
}
public void treeCollapsed(TreeExpansionEvent event) {
//do nothing
}
};
tree.addTreeExpansionListener(expListen);
tree.addTreeSelectionListener(new TreeSelectionListener() {
public void valueChanged(TreeSelectionEvent e) {
TreeAndTableNode node = (TreeAndTableNode)e.getPath().getLastPathComponent();
TableColumnModel colModel = table.getColumnModel();
int colCount = colModel.getColumnCount();
int[] width = new int[colCount];
TableCellRenderer[] render = new TableCellRenderer[colCount];
for (int i=0;i<colCount;i++) {
width[i] = colModel.getColumn(i).getWidth();
render[i] = colModel.getColumn(i).getCellRenderer();
}
updateTable(node);
for (int i=0;i<colCount;i++) {
colModel.getColumn(i).setPreferredWidth(width[i]);
colModel.getColumn(i).setCellRenderer(render[i]);
}
}
});
table = new JTable();
updateTable(null);
split = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, new JScrollPane(tree), new JScrollPane(table));
split.setDividerLocation(230);
buttons = new JPanel();
buTransfer = new JButton("download");
buTransfer.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
notifyChanges();
}
});
buMkdir = new JButton("mkdir");
buMkdir.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
RemoteFile dir = getSelectedDir();
if (dir == null) {
Dialogs.showMessage("Please choose a parent directory first");
} else {
String name = Dialogs.showInputDialog("Make Directory", "Make a new Directory in:\n"+dir.getFilnameWithPath()+"\n\nEnter new directory name:");
if (name!=null) {
if (name.startsWith("/")) {
Dialogs.showMessage("Directory name has to be relative.");
return;
}
//RemoteFile f = new RemoteFile(dir.getFilnameWithPath(), name, 0, System.currentTimeMillis(), true);
final TreePath path = tree.getSelectionPath();
String newDir = dir.getFilnameWithPath();
if (!newDir.endsWith("/")) {
newDir += "/";
}
newDir += name;
client.mkdir(newDir);
//refreshView(path);
}
}
}
});
buRemove = new JButton("remove");
buRemove.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
Vector<RemoteFile> files = getSelectedFiles();
if (files!=null && files.size()>0) {
//remove files
String msg = "Are you sure you want to remove the following files?";
for (RemoteFile f : files) {
msg += "\n"+f.getName();
}
int q = Dialogs.showYES_NO_Dialog("Remove Files", msg);
if (q == Dialogs.YES) {
for (RemoteFile f : files) {
client.delete(f.getFilnameWithPath());
}
// try {
// updateTable((TreeAndTableNode)tree.getSelectionPath().getLastPathComponent());
// } catch (Exception ex) {
// //ex.printStackTrace();
// try {
// updateTable(root);
// } catch (Exception ex2) {
// ex2.printStackTrace();
// }
// }
}
} else {
RemoteFile dir = getSelectedDir();
if (dir !=null) {
//remove dir
String msg = "Are you sure you want to remove the following directory?";
msg += "\n"+dir.getName();
int q = Dialogs.showYES_NO_Dialog("Remove Directory", msg);
if (q == Dialogs.YES) {
TreePath path = tree.getSelectionPath();
tree.setSelectionPath(path.getParentPath());
client.delete(dir.getFilnameWithPath());
// refreshView(path.getParentPath());
}
}
}
}
});
buRename = new JButton("rename");
buRename.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
RemoteFile from = null;
boolean isDir = false;
Vector<RemoteFile> files = getSelectedFiles();
if (files == null || files.size()==0) {
from = getSelectedDir();
isDir = true;
} else {
if (files.size()==1) {
from = files.get(0);
}
}
if (from==null) {
Dialogs.showMessage("Please select one file");
return;
}
String name = Dialogs.showInputDialog("Rename file", "Please enter new filename for file\n"+from.getName()+"\n",from.getName());
if (name!=null) {
//RemoteFile to = new RemoteFile(from.getPath(), name, from.getLength(), from.getLastModified(), false);
if (isDir) {
tree.setSelectionPath(tree.getSelectionPath().getParentPath());
}
client.rename(from.getFilnameWithPath(), name);
// updateTable((TreeAndTableNode)tree.getSelectionPath().getLastPathComponent());
}
}
});
buRefresh = new JButton("refresh");
buRefresh.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
refreshView(true);
}
});
}
public void refreshView(boolean updateTableFromServer) {
refreshView(tree.getSelectionPath(),updateTableFromServer);
}
public void refreshView(final TreePath path, final boolean updateTableFromServer) {
synchronized (objSync) {
Thread t = new Thread() {
public void run() {
TreeAndTableNode node = root;
if (path!=null) {
node = (TreeAndTableNode)path.getLastPathComponent();
System.out.println("refreshView :: "+Arrays.toString(path.getPath())); //TODO
}
try {
if (updateTableFromServer) {
node.files = null;
}
node.populateAgain();
tree.collapsePath(path);
tree.expandPath(path);
tree.setSelectionPath(path);
} catch (Exception ex) {
ex.printStackTrace();
}
try {
//updateTable((TreeAndTableNode)tree.getSelectionPath().getLastPathComponent());
updateTable(node);
} catch (Exception ex) {
ex.printStackTrace();
}
}
};
t.start();
// TreeAndTableNode node = root;
// if (path!=null) {
// node = (TreeAndTableNode)path.getLastPathComponent();
// System.out.println("refreshView :: "+Arrays.toString(path.getPath())); //TODO
// }
// try {
// node.populateAgain();
// tree.collapsePath(path);
// tree.expandPath(path);
// //tree.setSelectionPath(path);
// } catch (Exception ex) {
// ex.printStackTrace();
// }
// try {
// //updateTable((TreeAndTableNode)tree.getSelectionPath().getLastPathComponent());
// updateTable(node);
// } catch (Exception ex) {
// ex.printStackTrace();
// }
}
}
private String[] header = new String[] { "name", "type","size"};
private DefaultTableModel updateTableModel(TreeAndTableNode node) {
return new DefaultTableModel(buildTableModelData(node),header);
}
private String[][] buildTableModelData(TreeAndTableNode node) {
if (node == null) {
return new String[0][header.length];
}
try {
RemoteFile file = (RemoteFile) node.getUserObject();
if (node.files==null) {
node.files = list(file.getFilnameWithPath());
}
if (currentFiles==null) {
currentFiles = new Vector<RemoteFile>();
} else {
currentFiles.removeAllElements();
}
if (node.files==null || node.files.size()==0) {
return new String[0][header.length];
}
Vector<String[]> data = new Vector<String[]>();
for (RemoteFile f : node.files) {
if (!f.isDirectory()) {
currentFiles.add(f);
String[] d = new String[header.length];
d[0] = f.getName();
d[2] = (f.getLength() / 1000) + " kB";
d[1] = "";
int ind = d[0].lastIndexOf('.');
if (ind > 0 && ind + 1 < d[0].length()) {
d[1] = d[0].substring(ind + 1);
}
data.add(d);
}
}
String[][] tdata = new String[data.size()][header.length];
for (int i=0;i<data.size();i++) {
tdata[i] = data.get(i);
}
return tdata;
} catch (Exception ex) {
return new String[0][header.length];
}
}
public void updateTable() {
Thread t = new Thread() {
public void run() {
try {
TreeAndTableNode node = (TreeAndTableNode)tree.getSelectionPath().getLastPathComponent();
node.files = null; //fetch from server
updateTable(node);
} catch (Exception ex) {
ex.printStackTrace();
}
}
};
t.start();
}
private void updateTable(TreeAndTableNode node) {
if (table_model == null) {
table_model = updateTableModel(node);
RowSorter<TableModel> sorter = new TableRowSorter<TableModel>(table_model) {
private Comparator colSizeComparator = new Comparator<String>() {
public int compare(String s1, String s2) {
int i1 = Integer.parseInt(s1.substring(0,s1.length()-2).trim());
int i2 = Integer.parseInt(s2.substring(0,s2.length()-2).trim());
return i1-i2;
}
};
@Override
public Comparator<?> getComparator(int column) {
if (column==2) {
return colSizeComparator;
}
return super.getComparator(column);
}
};
table.setRowSorter(sorter);
table.setModel(table_model);
} else {
java.util.List<? extends SortKey> sortkeys = null;
if (table.getRowSorter()!=null) {
sortkeys = table.getRowSorter().getSortKeys();
}
table_model.setDataVector(buildTableModelData(node), header);
if (table.getRowSorter()!=null) {
table.getRowSorter().setSortKeys(sortkeys);
}
}
}
public RemoteFile getSelectedDir() {
try {
RemoteFile f = (RemoteFile)((TreeAndTableNode)tree.getSelectionPath().getLastPathComponent()).getUserObject();
return f;
} catch (Exception ex) {
//null pointer -> nothing selected
//ex.printStackTrace();
}
return null;
}
public Vector<RemoteFile> getSelectedFiles() {
Vector<RemoteFile> sel = new Vector<RemoteFile>();
int[] select = table.getSelectedRows();
// for (RemoteFile f : currentFiles) {
// System.out.println("currentFiles::"+f.getFilnameWithPath()+", "+f.isDirectory());
// }
if (select!=null && select.length>0) {
for (int i=0;i<select.length;i++) {
sel.add(currentFiles.get(table.getRowSorter().convertRowIndexToModel(select[i])));
//sel.add(currentFiles.get(select[i]));
}
}
return sel;
}
private void initLayout() {
this.setLayout(new BorderLayout());
this.add(split, BorderLayout.CENTER);
buttons.setLayout(new FlowLayout(FlowLayout.LEFT));
buttons.add(buTransfer);
buttons.add(buMkdir);
buttons.add(buRename);
buttons.add(buRemove);
buttons.add(buRefresh);
this.add(buttons, BorderLayout.SOUTH);
}
//observable
private Vector<MyObserver> observers = new Vector<MyObserver>();
public void addObserver(MyObserver observer) {
observers.add(observer);
}
public void notifyChanges() {
for (MyObserver ob : observers) {
ob.notifyChange(this);
}
}
}