DnD application problems

From: Andrey Fedorov (fedorov_at_optonline.net)
Date: 05/31/04


Date: 31 May 2004 10:45:21 -0700

I'm going crazy over java's implementation of drag-n-drop... i have
some basic (?) questions about some things that are confusing me...
This is the website I'm using to learn DnD:
http://java.sun.com/docs/books/tutorial/uiswing/misc/dnd.html

I'm trying to drag all of the information in a class 'Course' from a
'JList' to one of several 'JLabel's. Since a JList drags out a
'DataFlavor.stringFlavor' by default, I have to overload some things
in my own extension of 'TransferHandler', which I called
'CourseTransferHandler'.

 Quesion 1: Can I now '.setTransferHandler' this
'CourseTransferHandler' to both the JList I'm dragging from and the
JLabels I'm dragging to? Should I be using the same instance, or can I
just make a new one and save myself the variable(s)?

 Question 2: Since I'm using my own DataFlavor, the website mentioned
earlier says that "Transferring the data in this manner uses Object
serialization, so the class you use to transfer the data must
implement the Serializable interface, as must anything that is
serialized with it." I have therefore serialized my 'Course' class...
but another part of thet tut suggested I also make it Transferable...
I'm also not sure why although I've set my transfer handler to the
JLabels, enabled dragdropping with .setDragEnabled(true), and added a
mouse listener like the DragMouseAdapter on the page, they still don't
appear to drag-drop when I try to drag from them...

If anyone can help me out at all, I'd me more than gracious. If is
interested, here's the code in it's entirety:
/**
 * Copyright, 2004 Andrey Fedorov and Andrew Weintraub
 *
 * This file is part of MyHeroes: Student Hero
 * "Student Hero" 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 2, or (at your option)
 * any later version.

 * MyHeroes are distributed in the hope that they 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.

 * For a copy of the GNU GPL, please write to:
 * The Free Software Foundation, Inc., 59 Temple Place - Suite 330,
Boston,
 * MA 02111-1307, USA.
**/

package myhero;

import java.util.regex.*;
import java.net.*;
import java.util.*;

import javax.swing.*;
import javax.swing.event.*;
import java.awt.*;
import java.awt.event.*;
import java.awt.datatransfer.*;
import java.io.*;
import java.security.*;

/**
 * SetClassesPanel allows a user to choose the classes in their
schedule
 * @author Andrey Fedorov and Andrew Weintraub
 * @version 1.0
**/
class SetClassesPanel extends WizardPanel
                implements ListSelectionListener, DocumentListener, ActionListener {
        JTextField _limitTeach;
        JList _teachList;
        JList _courseList;
        DefaultListModel _teachModel, _courseModel;
        JPanel [] _columns;
        CourseField [] _courses;
        
        CourseTransferHandler _courseHandler;
        
        GetTeachersThread _teachThread;
        GetCourseThread _courseThread;
        
        Teacher[] _teachers;
        Course[] _course;
        WizardInterface _wizard;
        
        GetCourseThread _courseLoadedThread;
        
        public SetClassesPanel(WizardInterface wizard) {
                super();
                
                _wizard = wizard;
                _courseHandler = new CourseTransferHandler();
                
                _columns = new JPanel[4];
                
                //set up column 0
                _columns[0] = new JPanel();
                _columns[0].setOpaque(false);
                _columns[0].setLayout(new BoxLayout(_columns[0],
BoxLayout.PAGE_AXIS));
                _limitTeach = new JTextField();
                _limitTeach.setMaximumSize(new Dimension(200,100));
                _limitTeach.setText("Search:");
                _limitTeach.setEnabled(false);
                
                _teachList = new JList(_teachModel = new DefaultListModel());
                _teachList.setCellRenderer(new TeachCellRenderer());
                _teachList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
                JScrollPane teachListPane = new JScrollPane(_teachList);
                teachListPane.setOpaque(false);
                _columns[0].add(_limitTeach);
                _columns[0].add(teachListPane);
                
                //set up comlumn 1
                _columns[1] = new JPanel(new GridLayout(1,1));
                _columns[1].setOpaque(false);
                _courseList = new JList(_courseModel = new DefaultListModel());
                _courseList.setCellRenderer(new CourseCellRenderer());
                _courseList.setDragEnabled(true);
                //_courseList.addMouseListener(new DragMouseAdapter());
                //_courseList.setTransferHandler( new CourseTransferHandler() );
                _courseList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
                JScrollPane classListPane = new JScrollPane(_courseList);
                classListPane.setOpaque(false);
                _columns[1].add(classListPane);
                
                //set up column 2
                _columns[2] = new JPanel(new GridLayout(8,1));
                _columns[2].setOpaque(false);
                for(int i = 0; i<8; i++) {
                        JTextField temp = new JTextField("Period " + (i+1) + ":");
                        temp.setBorder(BorderFactory.createEmptyBorder(0,70,0,0));
                        temp.setDisabledTextColor(Color.black);
                        temp.setEnabled(false);
                        temp.setOpaque(false);
                        _columns[2].add(temp);
                }
                
                //set up column 3
                _columns[3] = new JPanel(new GridLayout(8,1));
                _columns[3].setOpaque(false);
                _courses = new CourseField[8];
                for(int i = 0; i<8; i++) {
                        _courses[i] = new CourseField("Study");
                        _courses[i].setHorizontalAlignment(JTextField.CENTER);
                        _courses[i].setDragEnabled(true);
                        _courses[i].setTransferHandler( _courseHandler );
                        _courses[i].addMouseListener(new DragMouseAdapter());
                        _courses[i].setEnabled(false);
                        _columns[3].add(_courses[i]);
                }
                                
                //set up main grid
                JPanel mainGrid = new JPanel(new GridLayout(0,4));
                mainGrid.setOpaque(false);
                for(int i = 0; i < 4; i++) {
                        mainGrid.add(_columns[i]);
                }
                
                //add everything to SetClassesPanel
                setLayout(new BorderLayout());
                add(new JLabel(new ImageIcon("./img/ScheduleTop.png")),
BorderLayout.NORTH);
                add(mainGrid, BorderLayout.CENTER);
                
                // Create and start the thread to get teachers
                _teachThread = new GetTeachersThread();
            _teachThread.start();
            
            _teachModel.addElement(new Teacher("Loading...", ""));
            _teachList.setEnabled(false);
            _teachers = new Teacher[] { new Teacher("Loading...", "") };
           }
        
        public void submit() {
                try {
                        FileInputStream userTmpFile = new FileInputStream("user.tmp");
                        ObjectInputStream userTmp = new ObjectInputStream(userTmpFile);
                        
                        FileOutputStream userListFile = new FileOutputStream("users.list",
true);
                        ObjectOutputStream userList = new ObjectOutputStream(userListFile);
                        
                        String user = (String)userTmp.readObject();
                        byte[] passMD5 = (byte[])userTmp.readObject();
                        
                        FileOutputStream classListFile = new FileOutputStream(user +
".classes");
                        ObjectOutputStream classList = new
ObjectOutputStream(classListFile);
                        
                        userTmpFile.close();
                        (new File("user.tmp")).delete();
                        
                        userList.writeObject(user);
                        userList.writeObject(passMD5);
                        userListFile.flush(); userListFile.close();
                        
                        // output classes list to classList
                        classListFile.flush(); classListFile.close();
                } catch (IOException e) {
                        JOptionPane.showMessageDialog(this, "Cannot find temp file.",
"Alert", JOptionPane.ERROR_MESSAGE);
                        _wizard.error();
                } catch (ClassNotFoundException e) {}
        }
        
        public void unInit() {}
        
        public void init() {
                _wizard.enableNext(false);
                _limitTeach.requestFocus();
        }
        
        public void valueChanged(ListSelectionEvent e) {
                if(!e.getValueIsAdjusting()){
                        // check if we are currently loading other course
                        if(_courseLoadedThread!=null && _courseLoadedThread.isAlive())
                                _courseLoadedThread.abort();
                        //make sure that an option is selected
                        if(_teachList.getSelectedValue()!=null) {
                                // add a loading sign, start a GetCourseThread
                                _courseModel.clear();
                            _courseModel.addElement(new Course("", "", "Loading..."));
                            
                                (_courseLoadedThread = new GetCourseThread()).start();
                                // ^ hot ^
                        }
                }
        }

        // DocumentListener
        public void changedUpdate(DocumentEvent e) {}
        public void insertUpdate(DocumentEvent e) {
                _teachModel.clear();
                for(int i = 0; i < _teachers.length; i++)
                        if(_teachers[i].getName().toLowerCase().indexOf(_limitTeach.getText().toLowerCase())!=-1)
                                _teachModel.addElement(_teachers[i]);
        }
        public void removeUpdate(DocumentEvent e) {
                _teachModel.clear();
                for(int i = 0; i < _teachers.length; i++)
                        if(_teachers[i].getName().toLowerCase().indexOf(_limitTeach.getText().toLowerCase())!=-1)
                                _teachModel.addElement(_teachers[i]);
        }
        
        // ActionListener
        public void actionPerformed( ActionEvent e ) {
                if(_teachModel.size() > 0) {
                        _teachList.requestFocus();
                        _teachList.setSelectedIndex(0);
                        valueChanged(new ListSelectionEvent(this, 0, 0, true));
                        _courseList.requestFocus();
                }
        }

        // called when teacher listing thread finishes
        private void teacherThreadDone() {
                _teachModel.clear();
                for(int i = 0; i < _teachers.length; i++) {
                        _teachModel.addElement(_teachers[i]);
                }
                
                _limitTeach.setText("");
                _limitTeach.setEnabled(true);
                _limitTeach.requestFocus();
                _limitTeach.getDocument().addDocumentListener( this );
                _limitTeach.addActionListener( this );

            _teachList.setEnabled(true);
                _teachList.getSelectionModel().addListSelectionListener( this );
        }
        
        // called when course listing thread finishes
        private void courseThreadDone() {
                _courseModel.clear();
                for(int i = 0; i < _course.length; i++) {
                        _courseModel.addElement(_course[i]);
                }
        }
        
        /**
         * Finds the names and URLs of all teachers on the Highlands
CourseHero webpage
         * @author Andrew Weintraub
         * @status lonely and miserable
         * @version 5/24/2004
        **/
        private Teacher[] parseTeachers(String school) throws IOException {
                String inputLine;
                String regex = "\\Q<A
HREF=\"\\E(\\Qhttp://www.homeworkhero.com/cgi-bin/aahero01/acceptit20/display.cgi?\\E\\w*\\Q+"+school+"\\E)\\Q\">\\E(?:<.+>)*([MSD]\\w{1,3}\\.?
[\\w'\\-() ]+?) ?(?:<.+>)*\\Q</font></A>\\E";
                Pattern teachLink = Pattern.compile(regex);
                Matcher matcher;

                LinkedList teachers = new LinkedList();
                URL highlandsTeachers = new
URL("http://www.homeworkhero.com/cgi-bin/aahero01/acceptit20/search.cgi?dir="+school);
                BufferedReader html = new BufferedReader(new
InputStreamReader(highlandsTeachers.openStream()));
                while(!html.ready()) {}

                while((inputLine = html.readLine()) !=null) {
                        matcher=teachLink.matcher(inputLine);
                        while(matcher.find()) {
                                teachers.add(new Teacher(matcher.group(2), matcher.group(1)));
                        }
                }

                return (Teacher[])teachers.toArray(new Teacher[0]);
        }

        private Course[] parseCourse(String url) throws IOException {
                
                return new Course[] {new Course("teacher", "url", "name"),new
Course("teacher2", "url2", "name2") };
        }
                
        private class Teacher {
                //private instance variables
                private String __URL;
                private String __Name;
                // option -> make class list a LinkedList
                private Date __LastUpdated;
        
                /**
                 * Constructor for CTeacher
                 * @param name the teacher's name
                 * @param URL the URL of the teacher's webpage
                **/
                public Teacher(String name, String URL) {
                        __URL = URL;
                        __Name = name;
                }
        
                /**
                 * gets the teacher's URL
                 * @returns the teacher's URL
                **/
                public String getURL() {
                        return __URL;
                }
                
                public String toString() {
                        return __Name;
                }
        
                /**
                 * gets the teacher's name
                 * @returns the teacher's name
                **/
                public String getName() {
                        return __Name;
                }
        }
        
        class Course implements Serializable
        {
                public Course(String teacher, String url, String name) {
                        __Name = name;
                        __URL = url;
                        __TeacherName = teacher;
                }
                                
                public String getName() {
                        return __Name;
                }
                
                public String getURL() {
                        return __URL;
                }
                
                public String getTeacher() {
                        return __TeacherName;
                }
                
                public String toString() {
                        return __Name+";"+__TeacherName+";"+__URL;
                }
                
                private String __Name;
                private String __URL;
                private String __TeacherName;
        }
        
        class CourseField extends JTextField {
                public CourseField(Course course) {
                        data = course;
                }
                
                public CourseField(String s) {
                        super(s);
                }
                
                public Course getCourse() {
                        return data;
                }
                
                Course data;
        }

        class TeachCellRenderer extends JLabel implements ListCellRenderer {
                public TeachCellRenderer() {
                        setOpaque(true);
            }
             
                public Component getListCellRendererComponent(
                        JList list,
                        Object value,
                        int index,
                        boolean isSelected,
                        boolean cellHasFocus)
                {
                        setText(((Teacher)value).getName());
                        setBackground(isSelected ? Color.lightGray : Color.white);
                        setForeground(isSelected ? Color.white : Color.black);
                        return this;
                }
        }
        
        class CourseCellRenderer extends JLabel implements ListCellRenderer {
                public CourseCellRenderer() {
                        setOpaque(true);
            }
             
                public Component getListCellRendererComponent(
                        JList list,
                        Object value,
                        int index,
                        boolean isSelected,
                        boolean cellHasFocus)
                {
                        setText(((Course)value).getName());
                        setBackground(isSelected ? Color.lightGray : Color.white);
                        setForeground(isSelected ? Color.white : Color.black);
                        return this;
                }
        }
        
        private class DragMouseAdapter extends MouseAdapter {
                public void mousePressed(MouseEvent e) {
                        JComponent c = (JComponent)e.getSource();
                        TransferHandler handler = c.getTransferHandler();
                        handler.exportAsDrag(c, e, TransferHandler.COPY);
                }
        }
        
        private class CourseTransferHandler extends TransferHandler {
                DataFlavor courseFlavor;
                
                CourseTransferHandler() {
                        courseFlavor = new DataFlavor(Course.class, "course");
                }

                public boolean importData(JComponent cmp, Transferable data) {
                        if (hasCourseFlavor(data.getTransferDataFlavors())) {
                                try {
                                        Course c = (Course)data.getTransferData(courseFlavor);
                                        ((JTextField)cmp).setText(c.getName());
                                        return true;
                                } catch (UnsupportedFlavorException ufe) {
                                } catch (IOException ioe) { }
                        }
                        return false;
                }

                protected boolean hasCourseFlavor(DataFlavor[] flavors) {
                        if (courseFlavor == null)
                                return false;
                
                        for (int i = 0; i < flavors.length; i++)
                                if (courseFlavor.equals(flavors[i]))
                                        return true;

                        return false;
                }

                public boolean canImport(JComponent c, DataFlavor[] flavors) {
                        return hasCourseFlavor(flavors);
                }

            protected Transferable createTransferable(JComponent c) {
                if (c instanceof CourseField) {
                    CourseField source = (CourseField)c;

                    return new TransferableCourse(source);
                }
                return null;
            }
            
                class TransferableCourse implements Transferable {
                        Course data;
                        
                        public TransferableCourse(CourseField a){
                                data = a.getCourse();
                        }
                        
                        public Object getTransferData(DataFlavor flavor) throws
UnsupportedFlavorException {
                                if(!isDataFlavorSupported(flavor))
                                        throw new UnsupportedFlavorException(flavor);
                                        
                                return data;
                        }
                        
                        public DataFlavor[] getTransferDataFlavors() {
                                return new DataFlavor[] { courseFlavor };
                        }
                        
                        public boolean isDataFlavorSupported(DataFlavor flavor) {
                                return flavor.equals(courseFlavor);
                        }
                }
        }
        
        class GetTeachersThread extends Thread {
                public void run() {
                        try {
                                _teachers = parseTeachers("nj_nhrhs");
                                System.out.println("Loaded " + _teachers.length + " teachers.");
        
                                SwingUtilities.invokeLater(new Runnable() {
                                        public void run() {
                                                teacherThreadDone();
                                        }
                                });
                        } catch(IOException e) {
                                if(JOptionPane.showConfirmDialog((Component)_wizard, "Unable to
connect to www.homeworkhero.com.\nPlease make sure you are connected
to the internet and try again.", "Alert",
JOptionPane.OK_CANCEL_OPTION,
JOptionPane.ERROR_MESSAGE)==JOptionPane.OK_OPTION) {
                                        run();
                                } else {
                                        _wizard.error();
                                }
                        }
                }
        }
        
        class GetCourseThread extends Thread {
                private boolean __abort = false;
                public void run() {
                        try {
                                // remeber what we started with...
                                String processingURL =
((Teacher)_teachList.getSelectedValue()).getURL();
                                //simulate delay
                                Thread.currentThread().sleep(3000);
                                
                                // start
                                Course [] courses = parseCourse(processingURL);
                                
                                // has no other teacher been selected?
                                if(processingURL.equals(((Teacher)_teachList.getSelectedValue()).getURL()))
{
                                        _course = courses;
                                        if(MyHero.debug)
                                                System.out.println("Loaded " + _course.length + "
assignments.");
                                        SwingUtilities.invokeLater(new Runnable() {
                                                public void run() {
                                                        courseThreadDone();
                                                }
                                        });
                                }
                        } catch(IOException e) {
                                if(JOptionPane.showConfirmDialog((Component)_wizard, "Unable to
connect to www.homeworkhero.com.\nPlease make sure you are connected
to the internet and try again.", "Alert",
JOptionPane.OK_CANCEL_OPTION,
JOptionPane.ERROR_MESSAGE)==JOptionPane.OK_OPTION) {
                                        run();
                                } else {
                                        _wizard.error();
                                }
                        } catch(InterruptedException e) {}
                }
                
                public void abort() {
                        __abort = true;
                }
        }
}