Reputation: 6532
Let me start by saying that I have been reading the drag'n drop tutorial and similar questions asked on SO, but unfortunately I have only gotten more confused about this matter. What I want to achieve is relatively simple so I am surprised that it got me in so much trouble already. I am writing a small utility application which will consolidate a bunch of result files (custom defined xml-type) into a large tab-separated text file. Most of the functionality is already coded, however I wanted to make a decent GUI for it.
What I want is to be able to drag'n drop files into a component (for instance JTextArea
) in a nice and gracious way (read: not full paths, but instead a small icon and name). I would like to be able to supply a JFileChooser
to browse for files as well. I will then parse the files sequentially to produce the matrix I want.
What I have learned so far is that the framework is already there however any additional functionality needs to be custom built. I have created a test GUI in Netbeans and tried to drag a bunch of files onto a JTextArea
, but they appear as file paths, and admittedly it looks very ugly.
I would really appreciate any tips and guidance on how to solve (or clarify) this problem in a neat way. Note that I do intend to use the software on multiple different OS (Mac,Win and Linux).
EDIT: the code I have so far is based on one of the examples from Sun tutorials and is as follows
import java.awt.datatransfer.*;
import java.awt.event.*;
import java.awt.*;
import java.io.*;
import javax.swing.*;
import javax.swing.UIManager.LookAndFeelInfo;
import javax.swing.border.TitledBorder;
import javax.swing.filechooser.FileNameExtensionFilter;
import javax.swing.text.*;
public class ConsolidatorDemo extends JPanel implements ActionListener {
private static final long serialVersionUID = -4487732343062917781L;
JFileChooser fc;
JButton clear;
JTextArea dropZone, console;
JSplitPane childSplitPane, parentSplitPane;
PrintStream ps;
public ConsolidatorDemo() {
super(new BorderLayout());
fc = new JFileChooser();;
fc.setMultiSelectionEnabled(true);
fc.setDragEnabled(true);
fc.setControlButtonsAreShown(false);
fc.setFileSelectionMode(JFileChooser.FILES_ONLY);
JPanel fcPanel = new JPanel(new BorderLayout());
fcPanel.add(fc, BorderLayout.CENTER);
clear = new JButton("Clear All");
clear.addActionListener(this);
JPanel buttonPanel = new JPanel(new BorderLayout());
buttonPanel.setBorder(BorderFactory.createEmptyBorder(5,5,5,5));
buttonPanel.add(clear, BorderLayout.LINE_END);
JPanel leftUpperPanel = new JPanel(new BorderLayout());
leftUpperPanel.setBorder(BorderFactory.createEmptyBorder(5,5,5,5));
leftUpperPanel.add(fcPanel, BorderLayout.CENTER);
leftUpperPanel.add(buttonPanel, BorderLayout.PAGE_END);
JScrollPane leftLowerPanel = new javax.swing.JScrollPane();
leftLowerPanel.setBorder(BorderFactory.createEmptyBorder(5,5,5,5));
dropZone = new JTextArea();
dropZone.setColumns(20);
dropZone.setLineWrap(true);
dropZone.setRows(5);
dropZone.setDragEnabled(true);
dropZone.setDropMode(javax.swing.DropMode.INSERT);
dropZone.setBorder(new TitledBorder("Selected files/folders"));
leftLowerPanel.setViewportView(dropZone);
childSplitPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT,
leftUpperPanel, leftLowerPanel);
childSplitPane.setDividerLocation(400);
childSplitPane.setPreferredSize(new Dimension(480, 650));
console = new JTextArea();
console.setColumns(40);
console.setLineWrap(true);
console.setBorder(new TitledBorder("Console"));
parentSplitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT,
childSplitPane, console);
parentSplitPane.setDividerLocation(480);
parentSplitPane.setPreferredSize(new Dimension(800, 650));
add(parentSplitPane, BorderLayout.CENTER);
}
public void setDefaultButton() {
getRootPane().setDefaultButton(clear);
}
public void actionPerformed(ActionEvent e) {
if (e.getSource() == clear) {
dropZone.setText("");
}
}
/**
* Create the GUI and show it. For thread safety,
* this method should be invoked from the
* event-dispatching thread.
*/
private static void createAndShowGUI() {
//Make sure we have nice window decorations.
JFrame.setDefaultLookAndFeelDecorated(true);
try {
//UIManager.setLookAndFeel("de.javasoft.plaf.synthetica.SyntheticaBlackStarLookAndFeel");
for (LookAndFeelInfo info : UIManager.getInstalledLookAndFeels()) {
if ("Nimbus".equals(info.getName())) {
UIManager.setLookAndFeel(info.getClassName());
break;
}
}
}catch (Exception e){
e.printStackTrace();
}
//Create and set up the window.
JFrame frame = new JFrame("Consolidator!");
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
//Create and set up the menu bar and content pane.
ConsolidatorDemo demo = new ConsolidatorDemo();
demo.setOpaque(true); //content panes must be opaque
frame.setContentPane(demo);
//Display the window.
frame.pack();
frame.setVisible(true);
demo.setDefaultButton();
}
public static void main(String[] args) {
//Schedule a job for the event-dispatching thread:
//creating and showing this application's GUI.
javax.swing.SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGUI();
}
});
}
Upvotes: 10
Views: 12554
Reputation: 51525
here's a quick snippet to import the actual Files into a JList (as opposed to importing its String representation into a text component) and use a custom renderer to present it nicely. It's adapted from the BasicDnD (in the tutorial):
fileDropper = new JList(new DefaultListModel());
fileDropper.setDragEnabled(true);
leftLowerPanel.setViewportView(fileDropper);
TransferHandler handler = new TransferHandler() {
@Override
public boolean canImport(TransferHandler.TransferSupport info) {
// we only import FileList
if (!info.isDataFlavorSupported(DataFlavor.javaFileListFlavor)) {
return false;
}
return true;
}
@Override
public boolean importData(TransferHandler.TransferSupport info) {
if (!info.isDrop()) {
return false;
}
// Check for FileList flavor
if (!info.isDataFlavorSupported(DataFlavor.javaFileListFlavor)) {
displayDropLocation("List doesn't accept a drop of this type.");
return false;
}
// Get the fileList that is being dropped.
Transferable t = info.getTransferable();
List<File> data;
try {
data = (List<File>)t.getTransferData(DataFlavor.javaFileListFlavor);
}
catch (Exception e) { return false; }
DefaultListModel model = (DefaultListModel) fileDropper.getModel();
for (File file : data) {
model.addElement(file);
}
return true;
}
private void displayDropLocation(String string) {
System.out.println(string);
}
};
fileDropper.setTransferHandler(handler);
fileDropper.setCellRenderer(new DefaultListRenderer(
StringValues.FILE_NAME, IconValues.FILE_ICON));
Couldn't resist to showing SwingX renderer config :-) In core java, you would do it manually, something like
class MyRenderer extends DefaultListCellRenderer {
@Override
public Component getListCellRendererComponent(...) {
super.getList...
if (value instanceof File) {
setText(FileSystemView.getFileSystemView().getSystemDisplayName(value);
setIcon(FileSystemView.getFileSystemView().getSystemIcon(value);
}
return this;
}
}
Upvotes: 11
Reputation: 1225
Here is the code without console and extra button
import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.Transferable;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File;
import java.io.PrintStream;
import java.util.List;
import javax.swing.BorderFactory;
import javax.swing.DefaultListCellRenderer;
import javax.swing.DefaultListModel;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JSplitPane;
import javax.swing.JTextArea;
import javax.swing.TransferHandler;
import javax.swing.UIManager;
import javax.swing.UIManager.LookAndFeelInfo;
import javax.swing.border.TitledBorder;
import javax.swing.filechooser.FileSystemView;
public class ConsolidatorDemo extends JPanel implements ActionListener {
private static final long serialVersionUID = -4487732343062917781L;
// JFileChooser fc;
JButton clear,compare;
JTextArea fc;
JList dropZone;
DefaultListModel listModel;
JSplitPane childSplitPane, parentSplitPane;
PrintStream ps;
public ConsolidatorDemo() {
super(new BorderLayout());
/* fc = new JFileChooser();;
fc.setMultiSelectionEnabled(true);
fc.setDragEnabled(true);
fc.setControlButtonsAreShown(false);
fc.setFileSelectionMode(JFileChooser.FILES_ONLY);*/
fc= new JTextArea();
fc.setText("Rules:\n1. drop old html first.\n2. drop new html.\n3. drop output folder.\n4. click compare button.\n5. Check output in the output.txt file.\nEnd");
fc.setEditable(false);
JPanel fcPanel = new JPanel(new BorderLayout());
fcPanel.add(fc, BorderLayout.CENTER);
compare = new JButton("Compare");
compare.addActionListener(this);
JPanel buttonPanel1 = new JPanel(new BorderLayout());
buttonPanel1.setBorder(BorderFactory.createEmptyBorder(1,1,1,1));
buttonPanel1.add(compare, BorderLayout.LINE_END);
clear = new JButton("Clear All");
clear.addActionListener(this);
JPanel buttonPanel = new JPanel(new BorderLayout());
buttonPanel.setBorder(BorderFactory.createEmptyBorder(1,1,1,1));
buttonPanel.add(clear, BorderLayout.LINE_END);
JPanel leftUpperPanel = new JPanel(new BorderLayout());
leftUpperPanel.setBorder(BorderFactory.createEmptyBorder(3,3,3,3));
leftUpperPanel.add(fcPanel, BorderLayout.CENTER);
leftUpperPanel.add(buttonPanel1, BorderLayout.LINE_END);
leftUpperPanel.add(buttonPanel, BorderLayout.PAGE_END);
JScrollPane leftLowerPanel = new javax.swing.JScrollPane();
leftLowerPanel.setBorder(BorderFactory.createEmptyBorder(3,3,3,3));
listModel = new DefaultListModel();
dropZone = new JList(listModel);
dropZone.setCellRenderer(new FileCellRenderer());
dropZone.setTransferHandler(new ListTransferHandler(dropZone));
dropZone.setDragEnabled(true);
dropZone.setDropMode(javax.swing.DropMode.INSERT);
dropZone.setBorder(new TitledBorder("Drag and drop files here"));
leftLowerPanel.setViewportView(new JScrollPane(dropZone));
childSplitPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT, leftLowerPanel,leftUpperPanel);
childSplitPane.setDividerLocation(200);//400
childSplitPane.setPreferredSize(new Dimension(300, 400));//480, 650
/*console = new JTextArea();
console.setColumns(40);
console.setLineWrap(true);
console.setBorder(new TitledBorder("Console"));
parentSplitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT,
childSplitPane, console);
parentSplitPane.setDividerLocation(480);
parentSplitPane.setPreferredSize(new Dimension(800, 650));*/
add(childSplitPane, BorderLayout.CENTER);
}
public void setDefaultButton() {
getRootPane().setDefaultButton(clear);
}
public void actionPerformed(ActionEvent e) {
if (e.getSource() == clear) {
listModel.clear();
}else if (e.getSource() == compare) {
//our function
}
}
/**
* Create the GUI and show it. For thread safety,
* this method should be invoked from the
* event-dispatching thread.
*/
private static void createAndShowGUI() {
//Make sure we have nice window decorations.
JFrame.setDefaultLookAndFeelDecorated(true);
try {
//UIManager.setLookAndFeel("de.javasoft.plaf.synthetica.SyntheticaBlackStarLookAndFeel");
for (LookAndFeelInfo info : UIManager.getInstalledLookAndFeels()) {
if ("Nimbus".equals(info.getName())) {
UIManager.setLookAndFeel(info.getClassName());
break;
}
}
}catch (Exception e){
e.printStackTrace();
}
//Create and set up the window.
JFrame frame = new JFrame("Bill of materials Comparer");
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
//Create and set up the menu bar and content pane.
ConsolidatorDemo demo = new ConsolidatorDemo();
demo.setOpaque(true); //content panes must be opaque
frame.setContentPane(demo);
//Display the window.
frame.pack();
frame.setVisible(true);
demo.setDefaultButton();
}
public static void main(String[] args) {
//Schedule a job for the event-dispatching thread:
//creating and showing this application's GUI.
javax.swing.SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGUI();
}
});
}
}
class FileCellRenderer extends DefaultListCellRenderer {
public Component getListCellRendererComponent(JList list,Object value,int index,boolean isSelected,boolean cellHasFocus) {
Component c = super.getListCellRendererComponent(list,value,index,isSelected,cellHasFocus);
if (c instanceof JLabel && value instanceof File) {
JLabel l = (JLabel)c;
File f = (File)value;
l.setIcon(FileSystemView.getFileSystemView().getSystemIcon(f));
l.setText(f.getName());
//l.setText(f.getAbsolutePath());
l.setToolTipText(f.getAbsolutePath());
}
return c;
}
}
class ListTransferHandler extends TransferHandler {
private JList list;
ListTransferHandler(JList list) {
this.list = list;
}
@Override
public boolean canImport(TransferHandler.TransferSupport info) {
// we only import FileList
if (!info.isDataFlavorSupported(DataFlavor.javaFileListFlavor)) {
return false;
}
return true;
}
@Override
public boolean importData(TransferHandler.TransferSupport info) {
if (!info.isDrop()) {
return false;
}
// Check for FileList flavor
if (!info.isDataFlavorSupported(DataFlavor.javaFileListFlavor)) {
displayDropLocation("List doesn't accept a drop of this type.");
return false;
}
// Get the fileList that is being dropped.
Transferable t = info.getTransferable();
List<File> data;
try {
data = (List<File>)t.getTransferData(DataFlavor.javaFileListFlavor);
}
catch (Exception e) { return false; }
DefaultListModel model = (DefaultListModel) list.getModel();
for (Object file : data) {
model.addElement((File)file);
}
return true;
}
private void displayDropLocation(String string) {
System.out.println(string);
}
}
Upvotes: 0
Reputation: 168825
This is effectively kleopatra's answer1 (with a few trivial changes, not necessarily for the better), ..with a screenshot!
import java.awt.datatransfer.*;
import java.awt.event.*;
import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Dimension;
import java.io.*;
import javax.swing.*;
import javax.swing.UIManager.LookAndFeelInfo;
import javax.swing.border.TitledBorder;
import javax.swing.filechooser.FileNameExtensionFilter;
import javax.swing.filechooser.FileSystemView;
import javax.swing.text.*;
import java.util.List;
public class ConsolidatorDemo extends JPanel implements ActionListener {
private static final long serialVersionUID = -4487732343062917781L;
JFileChooser fc;
JButton clear;
JTextArea console;
JList dropZone;
DefaultListModel listModel;
JSplitPane childSplitPane, parentSplitPane;
PrintStream ps;
public ConsolidatorDemo() {
super(new BorderLayout());
fc = new JFileChooser();;
fc.setMultiSelectionEnabled(true);
fc.setDragEnabled(true);
fc.setControlButtonsAreShown(false);
fc.setFileSelectionMode(JFileChooser.FILES_ONLY);
JPanel fcPanel = new JPanel(new BorderLayout());
fcPanel.add(fc, BorderLayout.CENTER);
clear = new JButton("Clear All");
clear.addActionListener(this);
JPanel buttonPanel = new JPanel(new BorderLayout());
buttonPanel.setBorder(BorderFactory.createEmptyBorder(5,5,5,5));
buttonPanel.add(clear, BorderLayout.LINE_END);
JPanel leftUpperPanel = new JPanel(new BorderLayout());
leftUpperPanel.setBorder(BorderFactory.createEmptyBorder(5,5,5,5));
leftUpperPanel.add(fcPanel, BorderLayout.CENTER);
leftUpperPanel.add(buttonPanel, BorderLayout.PAGE_END);
JScrollPane leftLowerPanel = new javax.swing.JScrollPane();
leftLowerPanel.setBorder(BorderFactory.createEmptyBorder(5,5,5,5));
listModel = new DefaultListModel();
dropZone = new JList(listModel);
dropZone.setCellRenderer(new FileCellRenderer());
dropZone.setTransferHandler(new ListTransferHandler(dropZone));
dropZone.setDragEnabled(true);
dropZone.setDropMode(javax.swing.DropMode.INSERT);
dropZone.setBorder(new TitledBorder("Selected files/folders"));
leftLowerPanel.setViewportView(new JScrollPane(dropZone));
childSplitPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT,
leftUpperPanel, leftLowerPanel);
childSplitPane.setDividerLocation(400);
childSplitPane.setPreferredSize(new Dimension(480, 650));
console = new JTextArea();
console.setColumns(40);
console.setLineWrap(true);
console.setBorder(new TitledBorder("Console"));
parentSplitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT,
childSplitPane, console);
parentSplitPane.setDividerLocation(480);
parentSplitPane.setPreferredSize(new Dimension(800, 650));
add(parentSplitPane, BorderLayout.CENTER);
}
public void setDefaultButton() {
getRootPane().setDefaultButton(clear);
}
public void actionPerformed(ActionEvent e) {
if (e.getSource() == clear) {
listModel.clear();
}
}
/**
* Create the GUI and show it. For thread safety,
* this method should be invoked from the
* event-dispatching thread.
*/
private static void createAndShowGUI() {
//Make sure we have nice window decorations.
JFrame.setDefaultLookAndFeelDecorated(true);
try {
//UIManager.setLookAndFeel("de.javasoft.plaf.synthetica.SyntheticaBlackStarLookAndFeel");
for (LookAndFeelInfo info : UIManager.getInstalledLookAndFeels()) {
if ("Nimbus".equals(info.getName())) {
UIManager.setLookAndFeel(info.getClassName());
break;
}
}
}catch (Exception e){
e.printStackTrace();
}
//Create and set up the window.
JFrame frame = new JFrame("Consolidator!");
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
//Create and set up the menu bar and content pane.
ConsolidatorDemo demo = new ConsolidatorDemo();
demo.setOpaque(true); //content panes must be opaque
frame.setContentPane(demo);
//Display the window.
frame.pack();
frame.setVisible(true);
demo.setDefaultButton();
}
public static void main(String[] args) {
//Schedule a job for the event-dispatching thread:
//creating and showing this application's GUI.
javax.swing.SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGUI();
}
});
}
}
class FileCellRenderer extends DefaultListCellRenderer {
public Component getListCellRendererComponent(JList list,
Object value,
int index,
boolean isSelected,
boolean cellHasFocus) {
Component c = super.getListCellRendererComponent(
list,value,index,isSelected,cellHasFocus);
if (c instanceof JLabel && value instanceof File) {
JLabel l = (JLabel)c;
File f = (File)value;
l.setIcon(FileSystemView.getFileSystemView().getSystemIcon(f));
l.setText(f.getName());
l.setToolTipText(f.getAbsolutePath());
}
return c;
}
}
class ListTransferHandler extends TransferHandler {
private JList list;
ListTransferHandler(JList list) {
this.list = list;
}
@Override
public boolean canImport(TransferHandler.TransferSupport info) {
// we only import FileList
if (!info.isDataFlavorSupported(DataFlavor.javaFileListFlavor)) {
return false;
}
return true;
}
@Override
public boolean importData(TransferHandler.TransferSupport info) {
if (!info.isDrop()) {
return false;
}
// Check for FileList flavor
if (!info.isDataFlavorSupported(DataFlavor.javaFileListFlavor)) {
displayDropLocation("List doesn't accept a drop of this type.");
return false;
}
// Get the fileList that is being dropped.
Transferable t = info.getTransferable();
List<File> data;
try {
data = (List<File>)t.getTransferData(DataFlavor.javaFileListFlavor);
}
catch (Exception e) { return false; }
DefaultListModel model = (DefaultListModel) list.getModel();
for (Object file : data) {
model.addElement((File)file);
}
return true;
}
private void displayDropLocation(String string) {
System.out.println(string);
}
}
TransferHandler
was very broken, I used hers. Though I used my version of the list cell renderer, which does not seem to capture the subtlety of what she was suggesting. I was also getting compilation errors on the mention of List
(given the compiler was assuming I meant an AWT list. I only realized what was happening after I'd changed several of the List<File>
type statements to be non-generic - I think most of them are now changed back.Upvotes: 8