Enable drop just for specific targets in Swing - java

I'm trying to implement drag & drop in java with two JList instances.
The basic flow works fine. However, when I'm dragging a string from one list, I want to restrict the drop target only for the second list.
I noticed that when I'm dragging a string from one list to my desktop so it creates a file containing this string.
Is there any way to avoid such situations?
public class SampleDnD extends JFrame{
public static void main(String[] args) {
new SampleDnD();
}
/**
* new form of frame sample contain 2 JLists with drag enabled.
*/
public SampleDnD(){
JList l1 = new JList();
JList l2 = new JList();
JScrollPane jScrollPane1 = new javax.swing.JScrollPane();
JScrollPane jScrollPane2 = new javax.swing.JScrollPane();
DefaultListModel listModel1 = new DefaultListModel();
DefaultListModel listModel2 = new DefaultListModel();
String[] list1 = new String[]{"1","3","5","7","9"};
String [] list2 = new String[]{"0","2","4","6","8"};
for(int index=0;index<list1.length;index++){
listModel1.add(index, list1[index]);
listModel2.add(index, list2[index]);
}
l1.setModel(listModel1);
l2.setModel(listModel2);
l1.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
l2.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
l1.setDropMode(DropMode.INSERT);
l1.setDragEnabled(true);
l2.setDragEnabled(true);
l1.setTransferHandler(new ListTransferHandler());
l2.setDropMode(DropMode.INSERT);
l2.setTransferHandler(new ListTransferHandler());
jScrollPane1.setViewportView(l1);
jScrollPane2.setViewportView(l2);
Container cp = getContentPane();
cp.setLayout(new BoxLayout(cp,BoxLayout.Y_AXIS));
setPreferredSize(new Dimension(800,500));
getContentPane().add(jScrollPane1);
getContentPane().add(jScrollPane2);
setVisible(true);
pack();
}
public class ListTransferHandler extends TransferHandler {
/**
* We only support importing strings.
*/
public boolean canImport(TransferHandler.TransferSupport info) {
// Check for String flavor
if (!info.isDataFlavorSupported(DataFlavor.stringFlavor)) {
return false;
}
return true;
}
/**
* Bundle up the selected items in a single list for export.
*
*/
protected Transferable createTransferable(JComponent c) {
JList list = (JList)c;
String value = (String)list.getSelectedValue();
return new StringSelection(value);
}
/**
* We support only move actions.
*/
public int getSourceActions(JComponent c) {
return TransferHandler.MOVE;
}
/**
* Perform the actual import. This demo only supports drag and drop.
*/
public boolean importData(TransferHandler.TransferSupport info) {
if (!info.isDrop()) {
return false;
}
JList list = (JList)info.getComponent();
DefaultListModel listModel = (DefaultListModel)list.getModel();
JList.DropLocation dl = (JList.DropLocation)info.getDropLocation();
int index = dl.getIndex();
boolean insert = dl.isInsert();
// Get the string that is being dropped.
Transferable t = info.getTransferable();
String data;
try {
data = (String)t.getTransferData(DataFlavor.stringFlavor);
}
catch (Exception e) { return false; }
if (insert) {
listModel.add(index++, data);
} else {
// If the items go beyond the end of the current
// list, add them in.
if (index < listModel.getSize()) {
listModel.set(index++, data);
} else {
listModel.add(index++, data);
}
}
return true;
}
/**
* Remove the items moved from the list.
*/
protected void exportDone(JComponent c, Transferable data, int action) {
JList source = (JList)c;
DefaultListModel listModel = (DefaultListModel)source.getModel();
if(action == TransferHandler.MOVE)
{
listModel.remove(source.getSelectedIndex());
}
}
}
}

A little late, but maybe helpful for somebody else:
You need to create your own DatFlavor, that will only be accepted by your own application.
DataFlavor myObjectDataFlavor = new DataFlavor(MyObject.class, "java/MyObject");
Pass this one instead of a default DataFlavor.javaFileListFlavor or DataFlavor.stringFlavor. Your class MyObject is a Pojo containing only a class variable of type String or an integer or whatever you need.

Related

empty TableModel after instantiating

Newbie seeking help please :-)
I am working on a little project to get familiar with Java desktop development and Database connectivity.
Attached code gives me an empty TableModel after instantiating therefore no data displayed in the JFrame.
Test class is instantiated from the menue of the main window with Test.showFrame();.
package ...
import ...
public class Test extends JPanel {
public Test() {
initializePanel();
}
private void initializePanel() {
// Creates an instance of TableModel
CategoryTableModel tableModel = new CategoryTableModel();
System.out.println(tableModel.getRowCount());
// Creates an instance of JTable with a TableModel
// as the constructor parameters.
JTable table = new JTable(tableModel);
table.setFillsViewportHeight(true);
JScrollPane scrollPane = new JScrollPane(table);
this.setPreferredSize(new Dimension(500, 200));
this.setLayout(new BorderLayout());
this.add(scrollPane, BorderLayout.CENTER);
}
public static void showFrame() {
JPanel panel = new Test();
panel.setOpaque(true);
JFrame frame = new JFrame("test");
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
frame.setContentPane(panel);
frame.pack();
frame.setVisible(true);
}
class CategoryTableModel extends AbstractTableModel {
private List<Category> all = null;
private Iterator<Category> iterator = null;
private int tableRowCount;
private TableModel tableModel;
public CategoryTableModel(){
Vector tableData = new Vector();
// TableModel's column names
Vector<String> tableHeaders = new Vector<String>();
tableHeaders.add("Category");
// Database call
all = new ReadCategory().allCategories();
// TableModel's data
for(Object o : all) {
Vector<Object> row = new Vector<Object>();
all.forEach((n) -> row.add(new Category().getName()));
tableData.add(row);
System.out.println("row added");
}
tableRowCount = tableData.size();
tableModel = new DefaultTableModel(tableData, tableHeaders);
System.out.println(tableModel.getRowCount());
}
#Override
public int getRowCount() {
return 0;
}
#Override
public int getColumnCount() {
return 0;
}
#Override
public Object getValueAt(int rowIndex, int columnIndex) {
return null;
}
}
}
Database call is fetching data via Hibernate and stores data in a .
Thanks for help.
In its most basic form a table model for a JTable defines the columns, the mapping of object to column and holds the data for the JTable to call upon. If we take your current table model and cut it down to fit this basic requirement we get the following.
import java.util.ArrayList;
import java.util.List;
import javax.swing.table.AbstractTableModel;
public class CategoryTableModel extends AbstractTableModel {
private final List<Category> tableData = new ArrayList<>();
public void add(Category cat) {
tableData.add(cat);
fireTableDataChanged();
}
#Override
public String getColumnName(int column) {
String result = "";
if (column == 0) {
result = "Category Name";
}
return result;
}
#Override
public int getRowCount() {
return tableData.size();
}
#Override
public int getColumnCount() {
return 1;
}
#Override
public Object getValueAt(int rowIndex, int columnIndex) {
if (columnIndex == 0) {
return tableData.get(rowIndex).getName();
}
return null;
}
}
Notice that we do not define any data in the model itself. All we define is some storage for the data and the column name of the single column that we require.
I have added an add() method that allows you to add data to the model (you may wish to define remove etc. yourself). When you add or remove data from the model you must always let the JTable know that the data has changed by calling fireTableDataChanged() so that the table can redraw itself.
To use this table model you will need to do
CategoryTableModel model = new CategoryTableModel();
model.add(new Category());
JTable table = new JTable(model);
You can replace the model.add() with a loop that iterates over your data and adds it to the model.

TreeView nodes names bugging

I'm having a lot of trouble dealing with TreeView, more specificcaly with the text shown per node.
Starting with an initializer for the Tree, where I expected a single node with the text, it might make the program more intuitive for the users:
private void defineEmpityTree(){
cbt.setModel(new DefaultTreeModel(
new DefaultMutableTreeNode("Relat\u00F3rios Individuais") {
{
}
}
));
}
and I add initialize the Tree:
cbt = new JCheckBoxTree();
defineEmpityTree();
scrollPane.setViewportView(cbt);
"Relatórios Individuais" aren't shown, Tree Ok, another bug
I ignored this problem and continued with the actual filling of the nodes, the user specifies his search and press "ok", thats when we get the Tree filled, 2nd image.
But then then another strange problem comes, if "ok" is pressed again, some texts go strange, 3rd image.
Heres the part of the cade where the nodes are created. For an array of files nodes are created for each type of report and each report lot, its not really important.
private void defineNewTree(ArrayList<File> files){
DefaultMutableTreeNode dmtRoot = new DefaultMutableTreeNode("Relat\u00F3rios Individuais");
DefaultMutableTreeNode dmtSubFolder = null;
DefaultMutableTreeNode dmtLotFolder = null;
int childsRoot = 0;
int childsSub = 0;
for(File f : files){
String subFolder = f.getName().substring(17,f.getName().length()-4);
String name = f.getName();
String lot = f.getName().substring(0, 3);
childsRoot = dmtRoot.getChildCount();
boolean subFoldExists = false;
boolean foldLotExists = false;
//creatingo folder reports:
for(int i = 0;i<childsRoot;i++){
if(dmtRoot.getChildAt(i).toString().equals(subFolder)){
dmtSubFolder = (DefaultMutableTreeNode) dmtRoot.getChildAt(i);
subFoldExists = true;
i=childsRoot;
}
}
if(!subFoldExists){
dmtSubFolder = new DefaultMutableTreeNode(subFolder);
dmtRoot.add(dmtSubFolder);
}
for(int j = 0;j<childsSub;j++){
if(dmtSubFolder.getChildAt(j).toString().equals(lot)){
dmtLotFolder = (DefaultMutableTreeNode) dmtSubFolder.getChildAt(j);
foldLotExists = true;
j=childsSub;
}
}
if(!foldLotExists){
dmtLotFolder = new DefaultMutableTreeNode("lote "+lot);
dmtSubFolder.add(dmtLotFolder);
}
dmtLotFolder.add(new DefaultMutableTreeNode(name));
}
DefaultTreeModel myTree = new DefaultTreeModel(dmtRoot);
cbt.setModel(myTree);
}
I think the real problem is that:
cbt.setModel(myTree);
Is that the correct way to define the Tree contents?
Edit.:
Button OK:
...
JButton btnOk = new JButton("OK");
btnOk.setBounds(161, 37, 49, 23);
btnOk.setActionCommand("ok");
btnOk.addActionListener(buttonListener);
panel.add(btnOk);
...
class ButtonListener implements ActionListener{
#Override
public void actionPerformed(ActionEvent a) {
switch(a.getActionCommand()){
case "ok":
search();
self.requestFocusInWindow();
break;
case "cancel":
dispose();
break;
case "print":
TreePath[] tp = cbt.getCheckedPaths();
for(TreePath t : tp){
System.out.println(t.getLastPathComponent().toString());
}
self.requestFocusInWindow();
break;
default:
break;
}
}
}
private void search(){
FileSeeker fs = new FileSeeker(textField.getText());
ArrayList<File> files = fs.getFiles();
defineNewTree(files);
}
Edit.:
CheckBoxCellRenderer:
private class CheckBoxCellRenderer extends JPanel implements TreeCellRenderer {
private static final long serialVersionUID = -7341833835878991719L;
JCheckBox checkBox;
public CheckBoxCellRenderer() {
super();
this.setLayout(new BorderLayout());
checkBox = new JCheckBox();
add(checkBox, BorderLayout.CENTER);
setOpaque(false);
}
#Override
public Component getTreeCellRendererComponent(JTree tree, Object value,
boolean selected, boolean expanded, boolean leaf, int row,
boolean hasFocus) {
DefaultMutableTreeNode node = (DefaultMutableTreeNode)value;
Object obj = node.getUserObject();
TreePath tp = new TreePath(node.getPath());
CheckedNode cn = nodesCheckingState.get(tp);
if (cn == null) {
return this;
}
checkBox.setSelected(cn.isSelected);
checkBox.setText(obj.toString());
checkBox.setOpaque(cn.isSelected && cn.hasChildren && ! cn.allChildrenSelected);
return this;
}
}
So, for the first problem the solution is:
private void defineEmpityTree(){
DefaultMutableTreeNode dmtn = new DefaultMutableTreeNode("Relat\u00F3rios Individuais");
cbt.setModel(new DefaultTreeModel(dmtn));
((DefaultTreeModel) cbt.getModel()).nodeChanged(dmtn);
}
I simply had to notify that the node had changed.
For the truncation problem,
cbt.setLargeModel(true);
did the trick.

How to use a variable which is used in an ActionListener?

as you can see in the picture I'm setting up a table which shows all files from a specific path (later I'll implement a filter for pdf files only). First, all files are simultaneously shown, but I want to see each single file of the path during the build up of the table. So I've changed it to a array list, but there a two errors which I really couldn't resolve...
Hope that you can help me ;-)
class FileModel extends AbstractTableModel implements FilenameFilter {
String titles[] = new String[] { "Path" };
Class<?> types[] = new Class[] { String.class, String.class };
private List<Object[]> data = new ArrayList<>();
public FileModel() {
this("C:\\");
}
public FileModel(String dir) {
File pwd = new File(dir);
setFileStats(pwd);
}
// Implement the methods of the TableModel interface we're interested
// in. Only getRowCount(), getColumnCount() and getValueAt() are
// required. The other methods tailor the look of the table.
#Override
public int getRowCount() {
return this.data.size();
}
#Override
public int getColumnCount() {
return this.titles.length;
}
#Override
public String getColumnName(int c) {
return this.titles[c];
}
#Override
public Class<?> getColumnClass(int c) {
return this.types[c];
}
#Override
public Object getValueAt(int r, int c) {
return this.data.get(r)[c];
}
// Our own method for setting/changing the current directory
// being displayed. This method fills the data set with file info
// from the given directory. It also fires an update event so this
// method could also be called after the table is on display.
public void setFileStats(File dir) {
System.out.println("SET MY DIR " + dir);
this.data = new ArrayList<>();
this.fireTableDataChanged();
String files[] = dir.list();
this.data = new Object[files.length][this.titles.length]; **// Here error #1**
for (int i = 0; i < files.length; i++) {
File tmp = new File(files[i]);
this.data[i][0] = tmp.getAbsolutePath(); **// Here error #2**
}
this.fireTableDataChanged();
}
#Override
public boolean accept(File dir, String name) {
// TODO Auto-generated method stub
return false;
}
}
Here's the code of the JFrame windows:
public class FileFrame extends JFrame {
protected FileModel fileModel = new FileModel();
{
this.setSize(500, 400);
setDefaultCloseOperation(EXIT_ON_CLOSE);
setLocationRelativeTo(null);
JTable FileTable = new JTable(this.fileModel);
TableRowSorter<TableModel> TableRowSorter = new TableRowSorter<TableModel>(this.fileModel);
FileTable.setRowSorter(TableRowSorter);
FileTable.setAutoResizeMode(JTable.AUTO_RESIZE_ALL_COLUMNS);
FileTable.setColumnSelectionAllowed(true);
FileTable.setDefaultRenderer(Number.class, new BigRenderer(1000));
JScrollPane JScrollPane = new JScrollPane(FileTable);
getContentPane().add(JScrollPane, BorderLayout.CENTER);
}
public static void main(String args[]) {
final FileFrame FileFrame = new FileFrame();
// Create menubar
JMenuBar menubar = new JMenuBar();
// Create JMenu object
JMenu menu = new JMenu("File");
// Create JMenuItem object
final JMenuItem openItem = new JMenuItem("Open");
JMenuItem exititem = new JMenuItem("Exit");
// Add JMenuItem to JMenu
menu.add(openItem);
menu.add(exititem);
// Add menu to menubar
menubar.add(menu);
// Add menubar to dialog
FileFrame.setJMenuBar(menubar);
// Show dialog
FileFrame.setVisible(true);
// Integrate ActionListener as anonymous class
openItem.addActionListener(new java.awt.event.ActionListener() {
public File savedPath;
public final JFileChooser FileChooser = new JFileChooser("C:\\");
// Initialize actionPerformed
#Override
public void actionPerformed(ActionEvent e) {
// Generate choose file
this.FileChooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
this.FileChooser.setDialogTitle("Selection of pdf directory");
this.FileChooser.setAcceptAllFileFilterUsed(false);
// Set the text
this.FileChooser.setApproveButtonText("Open directory");
// Set the tool tip
this.FileChooser.setApproveButtonToolTipText("Select pdf directory ");
if (this.savedPath != null)
this.FileChooser.setCurrentDirectory(this.savedPath);
int returnVal = this.FileChooser.showOpenDialog(openItem);
if (returnVal == JFileChooser.APPROVE_OPTION) {
this.savedPath = this.FileChooser.getSelectedFile();
FileFrame.fileModel.setFileStats(this.savedPath);
}
}
});
// Integrate ActionListener as anonymous class
exititem.addActionListener(new java.awt.event.ActionListener() {
// Initialize actionPerformed
#Override
public void actionPerformed(ActionEvent e) {
// Close program
System.exit(0);
}
});
}
}
The problem is you are trying to access data as though its an array
private List<Object[]> data = new ArrayList<>();
...
this.data = new Object[files.length][this.titles.length];
this.data[i][0] = tmp.getAbsolutePath();
But data is actually a List<Object[]>
So you might want to do
//data.add(new Object[files.length][this.titles.length]);
data.add(new Object[files.length]); // can only be one dimensional
and
((Object[])data.get(i))[0] = tmp.getAbsolutePath();
instead
See more on how to use Lists at The List Interface
UPDATE
Change your model code to this
String files[] = dir.list();
for (int i = 0; i < files.length; i++) {
File tmp = new File(files[i]);
data.add(new Object[] { tmp.getAbsolutePath()}) ;
}
All you need to do is add a new Object[] to the list (inside the loop) with just the file path, since that seems to be all you need. Don't add one before.

Java Swing using JList with an ArrayList<>

Basically, I've got a CD Library that holds instances of a CD ArrayList<>, I've then got a people ArrayList<> that can "borrow" CD's... from that CD's are added to an available, or unavailable list.
public CDStore(String storeNme) {
storeName = storeNme;
discsArray = new ArrayList<CD>();
peopleArray = new ArrayList<Person>();
}
By using a JList, I'm trying to make the elements of the list equal the instances of CD.
So... item1 in the list would be the CD at index 0, item 2 = index 1 and so on....
String[] entries = ??????????????????;
JList listOfCD = new JList(entries);
listOfCD.setVisibleRowCount(4);
JScrollPane listPane = new JScrollPane(listOfCD);
JTextField valueField = new JTextField("None", 7);
Thankyou.
Your CDStore could implement the interface ListModel. Than you can use the CDStore as a model for your JList.
CDStore store = new CDStore("Store");
// add some CDs
JList listOfCD = new JList(store);
listOfCD.setVisibleRowCount(4);
JScrollPane listPane = new JScrollPane(listOfCD);
JTextField valueField = new JTextField("None", 7);
Here is example implementation of CDStore implements ListModel. Everytime the discsArray changes you should call the method fireContentsChanged.
public class CDStore implements ListModel {
private String storeName;
private List<CD> discsArray;
private List<Person> peopleArray;
public CDStore(String storeNme) {
storeName = storeNme;
discsArray = new ArrayList<CD>();
peopleArray = new ArrayList<Person>();
}
//your methods
//ListModel
private List<ListDataListener> listener = new ArrayList<ListDataListener>();
public void addListDataListener(ListDataListener l) {
listener.add(l);
}
public void removeListDataListener(ListDataListener l) {
listener.remove(l);
}
protected void fireContentsChanged() {
for (ListDataListener l : listener) {
l.contentsChanged(new ListDataEvent(this, ListDataEvent.CONTENTS_CHANGED, 0, discsArray.size()-1));
}
}
public Object getElementAt(int index) {
return discsArray.get(index);
}
public int getSize() {
return discsArray.size()
}
}
If you have a method to retrieve CD name, let's call it getCDName(),
you can try
String[] entries = new String[discsArray.length];
int index = 0;
for (CD cd: discsArray) {
entries[index++] = cd.getCDName();
}
This should fill in your entries array.

Dragging node from JTree and dropping onto JLabel to perform an action

I want to implement DnD on my system by dragging a node from a JTree and dropping it onto a JLabel.
The JLabel is an Icon with certain properties about a machine, and by dragging the information from the JTree node onto the JLabel I want it to be able to send a message to a client listening on that machine.
Any help is much appreciated!
Example of label method:
private void makeLabel(String html, final String version) {
// Create a button to link to the DR environment
//JButton button = new JButton(html);
JLabel machineLabel = new JLabel();
machineLabel.setTransferHandler(new TransferHandler("text"));
MouseListener listener = new DragMouseAdapter();
machineLabel.addMouseListener(listener);
machineLabel.setIcon(onlineIcon);
machineLabel.setToolTipText("IP: " + html);
//Add the button to the panel and make sure it appears
machineLabel.setSize(25, 10);
vecIcons.addElement(machineLabel);
buttonPanel.add(machineLabel);
buttonPanel.setVisible(true);
buttonPanel.validate();
dynaScrollPane.validate();
buttonPanel.repaint();
dynaScrollPane.repaint();
}
DragMouseAdapter method:
private class DragMouseAdapter extends MouseAdapter {
public void mousePressed(MouseEvent e) {
JComponent c = (JComponent) e.getSource();
TransferHandler handler = c.getTransferHandler();
handler.exportAsDrag(c, e, TransferHandler.LINK);
}
}
Then in order to make my tree draggable I just have:
exampleTree.setDragEnabled(true);
not sure I understand your setup: assuming you want the label to be a drop target, simply implement a custom Transferhandler which accepts the dataflavour as exported by the tree and do something with it
EDIT
To get hold of the TreePath use a custom Transferhandler on the tree as well: override its createTransferable which returns the TreePath:
final DataFlavor flavor =new DataFlavor(TreePath.class, "treePath");
TransferHandler treeHandler = new TransferHandler() {
DataFlavor[] pathFlavour = new DataFlavor[]
{flavor};
/**
* #inherited <p>
*/
#Override
protected Transferable createTransferable(JComponent c) {
JTree tree = (JTree) c;
final TreePath path = tree.getSelectionPath();
Transferable transferable = new Transferable() {
#Override
public DataFlavor[] getTransferDataFlavors() {
return pathFlavour;
}
#Override
public boolean isDataFlavorSupported(DataFlavor flavor) {
return pathFlavour[0].equals(flavor);
}
#Override
public Object getTransferData(DataFlavor flavor)
throws UnsupportedFlavorException, IOException {
return path;
}
};
return transferable;
}
public int getSourceActions(JComponent c) {
return COPY;
}
};
tree.setTransferHandler(treeHandler);
JLabel labelTarget = new JLabel("I'm a drop target!");
TransferHandler labelHandler = new TransferHandler() {
/**
* #inherited <p>
*/
#Override
public boolean importData(JComponent comp, Transferable t) {
try {
LOG.info("import from: " + t.getTransferData(flavor));
} catch (Exception e) {
e.printStackTrace();
}
return true;
}
/**
* #inherited <p>
*/
#Override
public boolean canImport(JComponent comp,
DataFlavor[] transferFlavors) {
return true;
}
};
labelTarget.setTransferHandler(labelHandler);
no need for an additional mouseListener

Categories