/*
 * Copyright (c) 2007-2010 by The Broad Institute, Inc. and the Massachusetts Institute of Technology.
 * All Rights Reserved.
 *
 * This software is licensed under the terms of the GNU Lesser General Public License (LGPL), Version 2.1 which
 * is available at http://www.opensource.org/licenses/lgpl-2.1.php.
 *
 * THE SOFTWARE IS PROVIDED "AS IS." THE BROAD AND MIT MAKE NO REPRESENTATIONS OR WARRANTIES OF
 * ANY KIND CONCERNING THE SOFTWARE, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT
 * OR OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE.  IN NO EVENT SHALL THE BROAD OR MIT, OR THEIR
 * RESPECTIVE TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, AND AFFILIATES BE LIABLE FOR ANY DAMAGES OF
 * ANY KIND, INCLUDING, WITHOUT LIMITATION, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ECONOMIC
 * DAMAGES OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER THE BROAD OR MIT SHALL
 * BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE POSSIBILITY OF THE
 * FOREGOING.
 */
/*
 * TrackPanel.java
 *
 * Created on Sep 5, 2007, 4:09:39 PM
 *
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package org.broad.igv.ui.panel;

//~--- non-JDK imports --------------------------------------------------------

import org.apache.log4j.Logger;
import org.broad.igv.PreferenceManager;
import org.broad.igv.exceptions.DataLoadException;
import org.broad.igv.renderer.DataRange;
import org.broad.igv.session.ViewContext;
import org.broad.igv.track.RenderContext;
import org.broad.igv.track.Track;
import org.broad.igv.track.TrackGroup;
import org.broad.igv.ui.*;
import org.broad.igv.ui.util.MessageUtils;
import org.broad.igv.ui.util.Packable;
import org.broad.igv.ui.util.UIUtilities;
import org.broad.igv.util.ResourceLocator;

import javax.swing.*;
import javax.swing.event.MouseInputAdapter;
import java.awt.*;
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.Transferable;
import java.awt.dnd.*;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.MouseEvent;
import java.io.File;
import java.text.DecimalFormat;
import java.util.*;
import java.util.List;

/**
 * @author jrobinso
 */
public class DataPanel extends TrackPanelComponent {

    private static Logger log = Logger.getLogger(DataPanel.class);

    // TODO move this to some central place
    final static private boolean IS_MAC = System.getProperty("os.name").toLowerCase().startsWith("mac");


    private PanAndZoomTool panAndZoomTool;
    private IGVTool currentTool;
    private IGVTool previousTool;
    private IGVTool defaultTool;
    private Point tooltipTextPosition;

    /**
     * Constructs ...
     */
    public DataPanel(TrackPanel trackPanel) {
        super(trackPanel);
        init();
        setFocusable(true);
        setAutoscrolls(true);
        setToolTipText("Data panel");

        DropTarget target = new DropTarget(this, new FileDropTargetListener(trackPanel));
        setDropTarget(target);
        target.setActive(true);
    }

    /**
     * @return the panAndZoomTool
     */
    public PanAndZoomTool getPanAndZoomTool() {
        return panAndZoomTool;
    }


    /**
     * @return
     */
    public JScrollBar getVerticalScrollbar() {
        Component sp = getParent();
        while (sp != null && !(sp instanceof JScrollPane)) {
            sp = sp.getParent();
        }
        return sp == null ? null : ((JScrollPane) sp).getVerticalScrollBar();
    }

    /**
     * Method description
     *
     * @param tool
     */
    public void setCurrentTool(final IGVTool tool) {

        UIUtilities.invokeOnEventThread(new Runnable() {

            public void run() {

                if (defaultTool == null) {
                    defaultTool = tool;
                }

                // Swap out old tool
                if (currentTool != null) {
                    removeKeyListener(currentTool);
                    removeMouseListener(currentTool);
                    removeMouseMotionListener(currentTool);
                }

                // Swap in new tool
                currentTool = ((tool == null) ? defaultTool : tool);
                if (currentTool != null) {
                    currentTool.showCursor();
                    addKeyListener(currentTool);
                    addMouseListener(currentTool);
                    addMouseMotionListener(currentTool);
                }
            }
        });
    }

    /**
     * Method description
     *
     * @return
     */
    public IGVTool getCurrentTool() {
        return currentTool;
    }

    // final Stack<Integer> tilesToLoadStack = new Stack();

    /**
     * Method description
     *
     * @param g
     */
    @Override
    public void paintComponent(final Graphics g) {

        //if (log.isDebugEnabled()) {
        //    log.debug("Start paintComponent " + getName());
        //}
        super.paintComponent(g);

        RenderContext context = null;
        try {

            Graphics2D graphics2D = (Graphics2D) g.create();
            double scale = getViewContext().getScale();
            String chromosomeName = getViewContext().getChrName();
            double startLocation = getViewContext().getOrigin();
            double endLocation = (startLocation + getWidth() * scale + 1);
            int zoom = getViewContext().getZoom();
            String genomeId = getViewContext().getGenomeId();

            context = new RenderContext(genomeId, chromosomeName, startLocation, endLocation,
                    zoom, scale, getVisibleRect(), graphics2D);

            if (IS_MAC) {
                this.applyMacPerformanceHints((Graphics2D) g);
            }


            graphics2D.setBackground(getBackground());
            //graphics2D.clearRect(0, 0, getWidth(), getHeight());

            final Collection<TrackGroup> groups = new ArrayList(getTrackGroups());

            // If there are no tracks to paint, just exit
            boolean hasTracks = false;
            for (TrackGroup group : groups) {
                if (group.getTracks().size() > 0) {
                    hasTracks = true;
                    break;
                }
            }
            if (!hasTracks) {
                removeMousableRegions();
                return;
            }


            int trackWidth = getWidth();
            int trackHeight = getHeight();
            DataPanelPainter painter = new DataPanelPainter();
            painter.paint(groups, context, trackWidth, trackHeight,
                    getBackground(),
                    getVisibleRect());

            // Compute mouse senstive regions
            computeMousableRegions(groups);

            // Finally draw y axis.
            int trackX = 0;
            int trackY = 0;
            for (Iterator<TrackGroup> groupIter = groups.iterator(); groupIter.hasNext();) {

                TrackGroup group = groupIter.next();
                if (groups.size() > 1) {
                    trackY += UIConstants.groupGap;
                }

                // Draw borders
                for (Track track : group.getTracks()) {
                    if (track.isVisible()) {
                        Rectangle rect = new Rectangle(trackX, trackY, getWidth(), track.getHeight());
                        track.renderAxis(context, rect);
                        trackY += track.getHeight();
                    }
                }
            }

            // If there is a partial ROI in progress draw it first
            if (currentTool instanceof RegionOfInterestTool) {
                int startLoc = ((RegionOfInterestTool) currentTool).getRoiStart();
                if (startLoc > 0) {
                    int start = getViewContext().getPixelPosition(startLoc);
                    g.setColor(Color.BLACK);
                    graphics2D.drawLine(start, 0, start, getHeight());
                }
            }


            if (IGVMainFrame.getInstance().isShowRegionsOfInterestBarsOn()) {
                drawAllRegions(g);
            }

        } finally {

            if (context != null) {
                context.dispose();
            }
        }
    }

    /**
     * Method description
     *
     * @param g
     */
    public void drawAllRegions(final Graphics g) {

        Collection<RegionOfInterest> regions =
                IGVMainFrame.getInstance().getSession().getRegionsOfInterest(getViewContext().getChrName());

        if ((regions == null) || regions.isEmpty()) {
            return;
        }

        boolean drawBars = PreferenceManager.getInstance().isShowRegionBars();
        Graphics2D graphics2D = (Graphics2D) g.create();
        try {

            for (RegionOfInterest regionOfInterest : regions) {
                if (drawBars || regionOfInterest == IGVMainFrame.getInstance().getSelectedRegion()) {
                    drawRegion(graphics2D, regionOfInterest);
                }
            }
        } finally {
            if (graphics2D != null) {
                graphics2D.dispose();
            }
        }
    }

    private boolean drawRegion(Graphics2D graphics2D, RegionOfInterest regionOfInterest) {
        Integer regionStart = regionOfInterest.getStart();
        if (regionStart == null) {
            return true;
        }

        Integer regionEnd = regionOfInterest.getEnd();
        if (regionEnd == null) {
            regionEnd = regionStart;
        }
        ViewContext viewContext = getViewContext();
        int start = viewContext.getPixelPosition(regionStart);
        int end = viewContext.getPixelPosition(regionEnd);

        // Set foreground color of boundaries
        int height = getHeight();
        graphics2D.setColor(regionOfInterest.getForegroundColor());
        graphics2D.drawLine(start, 0, start, height);
        graphics2D.drawLine(end, 0, end, height);
        return false;
    }

    protected String generateTileKey(final String chr, int t,
                                     final int zoomLevel) {

        // Fetch image for this chromosome, zoomlevel, and tile.  If found
        // draw immediately
        final String key = chr + "_z_" + zoomLevel + "_t_" + t;
        return key;
    }

    /**
     * TODO -- it is assumed the tracks are drawn in the same order for the
     * image.  This is brittle, the rects could be computed when the image
     * is drawn.  Really the y position and height are all that is needed.
     *
     * @param groups
     */
    private void computeMousableRegions(Collection<TrackGroup> groups) {

        removeMousableRegions();

        int trackX = 0;
        int trackY = 0;

        for (Iterator<TrackGroup> groupIter = groups.iterator(); groupIter.hasNext();) {
            TrackGroup group = groupIter.next();

            if (groups.size() > 1) {
                trackY += UIConstants.groupGap;
            }
            for (Track track : group.getTracks()) {

                if (track.isVisible()) {

                    int trackWidth = getWidth();
                    int trackHeight = track.getHeight();

                    Rectangle actualAreaOnDataPanel = new Rectangle(trackX,
                            trackY, (trackWidth),
                            (trackHeight));

                    addMousableRegion(new MouseableRegion(actualAreaOnDataPanel,
                            track));

                    trackY += track.getHeight();
                }
            }
        }

    }

    /**
     * Does an appropriate resize of track components 'without' a repaint.
     */
    public void doResize() {

        UIUtilities.invokeOnEventThread(new Runnable() {

            public void run() {

                // Get the view that holds the track name, attribute and data panels
                final TrackPanel trackView = (TrackPanel) getParent();

                if (trackView == null) {
                    return;
                }

                final int newHeight = calculateResizeHeight();

                // The height needs to be done on the swing thread.
                trackView.setHeight(newHeight);

            }
        });
    }

    /**
     * Do not remove - Used for debugging only
     *
     * @param trackName
     */
    public void debugDump(String trackName) {

        // Get the view that holds the track name, attribute and data panels
        TrackPanel trackView = (TrackPanel) getParent();

        if (trackView == null) {
            return;
        }


        if (trackView.hasTracks()) {
            String name = this.getTrackSetID().toString();
            System.out.println(
                    "\n\n" + name + " Track COUNT:" + trackView.getTracks().size());
            System.out.println(
                    "\t\t\t\t" + name + " scrollpane height     = " + trackView.getScrollPane().getHeight());
            System.out.println(
                    "\t\t\t\t" + name + " viewport height       = " + trackView.getViewportHeight());
            System.out.println(
                    "\t\t\t\t" + name + " TrackView min height  = " + trackView.getMinimumSize().getHeight());
            System.out.println(
                    "\t\t\t\t" + name + " TrackView pref height = " + trackView.getPreferredSize().getHeight());
            System.out.println(
                    "\t\t\t\t" + name + " TrackView height      = " + trackView.getSize().getHeight());
        }

    }

    /**
     * Return html formatted text for mouse position (pixels).
     * TODO  this will be a lot easier when each track has its own panel.
     */
    static DecimalFormat locationFormatter = new DecimalFormat();

    /**
     * Method description
     *
     * @param x
     * @param y
     * @return
     */
    public Track getTrack(int x, int y) {
        for (MouseableRegion mouseRegion : getTrackRegions()) {
            if (mouseRegion.containsPoint(x, y)) {
                return mouseRegion.getTracks().iterator().next();
            }
        }
        return null;

    }

    /**
     * Method description
     *
     * @param x
     * @param y
     */
    public void updateTooltipText(int x, int y) {
        StringBuffer popupTextBuffer = new StringBuffer();
        double location = getViewContext().getChromosomePosition(x);
        double displayLocation = location + 1;

        popupTextBuffer.append("<html>");

        Track track = null;
        List<MouseableRegion> regions = this.getTrackRegions();
        for (MouseableRegion mouseRegion : regions) {
            if (mouseRegion.containsPoint(x, y)) {
                track = mouseRegion.getTracks().iterator().next();
                if (track != null) {

                    // Calculate y relative to track bounds
                    int yRelative = y - mouseRegion.getBounds().y;

                    // First see if there is an overlay track.  If there is, give
                    // it first crack
                    List<Track> overlays = IGVMainFrame.getInstance().getTrackManager().getOverlayTracks(track);

                    if (overlays != null) {
                        for (Track overlay : overlays) {
                            if ((overlay != track) && (overlay.getValueStringAt(
                                    getViewContext().getChrName(),
                                    displayLocation, yRelative) != null)) {
                                popupTextBuffer.append(getPopUpText(overlay, displayLocation, yRelative));
                                popupTextBuffer.append("<br>");
                            }
                        }
                    }

                    popupTextBuffer.append(getPopUpText(track, displayLocation, y));
                }
                break;
            }
        }

        popupTextBuffer.append("<br>Location: ");
        popupTextBuffer.append(locationFormatter.format((int) displayLocation));

        setToolTipText(popupTextBuffer.toString());
    }

    private boolean isWaitingForToolTipText = false;


    @Override
    final public String getToolTipText() {

        if (!isWaitingForToolTipText) {
            isWaitingForToolTipText = true;
            if (tooltipTextPosition != null) {
                updateTooltipText(tooltipTextPosition.x, tooltipTextPosition.y);
            }
            isWaitingForToolTipText = false;
        }
        return super.getToolTipText();
    }


    public String getPopupMenuTitle(int x, int y) {

        Collection<Track> tracks = getSelectedTracks();
        String popupTitle = "";

        // Title for the popup
        if (!tracks.isEmpty()) {
            for (Track track : tracks) {
                popupTitle = track.getName();
                break;
            }
        }
        return popupTitle;
    }

    /**
     * Return the
     *
     * @return descriptive text for the panel at the given location.
     *         Note:  the coordinates are in panel coordinates, they must be
     *         offset by the track retangle.  This will not be needed when each
     *         track has its own panel.
     */
    String getPopUpText(Track track, double location, int y) {

        DataRange axisDefinition = track.getDataRange();
        StringBuffer buf = new StringBuffer();
        String value = track.getValueStringAt(getViewContext().getChrName(), location, y);
        if (value != null) {
            buf.append(value);
        }
        return buf.toString();
    }


    // TODO -- this method and the key and mouse adapters are nearly identical
    // for the DataPanel and FeaturePanel classes.  Refractor to combine those
    // classes, or share this method in some other way.

    private void init() {
        panAndZoomTool = new PanAndZoomTool(this);
        setCurrentTool(getPanAndZoomTool());
        setBackground(new java.awt.Color(255, 255, 255));
        setBorder(javax.swing.BorderFactory.createLineBorder(new java.awt.Color(0, 0, 0)));
        setMinimumSize(new java.awt.Dimension(700, 0));
        setRequestFocusEnabled(false);

        org.jdesktop.layout.GroupLayout featureTrackPanelLayout = new org.jdesktop.layout.GroupLayout(
                this);
        setLayout(featureTrackPanelLayout);
        featureTrackPanelLayout.setHorizontalGroup(
                featureTrackPanelLayout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING).add(
                        0, 708, Short.MAX_VALUE));
        featureTrackPanelLayout.setVerticalGroup(
                featureTrackPanelLayout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING).add(
                        0, 228, Short.MAX_VALUE));

        // Key Events
        KeyAdapter keyAdapter = new KeyAdapter() {

            @Override
            public void keyPressed(KeyEvent e) {
                if (e.getKeyChar() == '+' || e.getKeyCode() == KeyEvent.VK_PLUS) {
                    WaitCursorManager.CursorToken token = WaitCursorManager.showWaitCursor();
                    try {
                        getViewContext().incrementZoom(1);
                    } finally {
                        WaitCursorManager.removeWaitCursor(token);
                    }
                } else if (e.getKeyChar() == '-' || e.getKeyCode() == KeyEvent.VK_PLUS) {
                    WaitCursorManager.CursorToken token = WaitCursorManager.showWaitCursor();
                    try {
                        getViewContext().incrementZoom(-1);
                    } finally {
                        WaitCursorManager.removeWaitCursor(token);
                    }

                } else if (e.getKeyCode() == KeyEvent.VK_RIGHT) {
                    getViewContext().shiftOriginPixels(5);
                } else if (e.getKeyCode() == KeyEvent.VK_LEFT) {
                    getViewContext().shiftOriginPixels(-5);
                } else if (e.getKeyCode() == KeyEvent.VK_HOME) {
                    WaitCursorManager.CursorToken token = WaitCursorManager.showWaitCursor();
                    try {
                        getViewContext().shiftOriginPixels(-getWidth());
                        getViewContext().recordHistory();
                    } finally {
                        WaitCursorManager.removeWaitCursor(token);
                    }


                } else if (e.getKeyCode() == KeyEvent.VK_END) {
                    WaitCursorManager.CursorToken token = WaitCursorManager.showWaitCursor();
                    try {
                        getViewContext().shiftOriginPixels(getWidth());
                        getViewContext().recordHistory();
                    } finally {
                        WaitCursorManager.removeWaitCursor(token);
                    }
                } else if (e.getKeyCode() == KeyEvent.VK_PLUS) {
                } else if (e.getKeyCode() == KeyEvent.VK_MINUS) {
                }


            }
        };
        addKeyListener(keyAdapter);


        // Mouse Events
        MouseInputAdapter mouseAdapter = new DataPanelMouseAdapter();

        addMouseMotionListener(mouseAdapter);
        addMouseListener(mouseAdapter);
    }

    /**
     * Method description
     *
     * @param x
     * @param y
     * @param width
     * @param height
     */
    @Override
    public void setBounds(int x, int y, int width, int height) {

        int adjustedX = getAdjustedX();
        int adjustedWidth = getAdjustedWidth();
        super.setBounds(adjustedX, y, adjustedWidth, height);
    }

    /**
     * Method description
     *
     * @param r
     */
    @Override
    public void setBounds(Rectangle r) {

        int adjustedX = getAdjustedX();
        int adjustedWidth = getAdjustedWidth();
        super.setBounds(new Rectangle(adjustedX, r.y, adjustedWidth, r.height));
    }

    /**
     * Method description
     *
     * @param width
     * @param height
     */
    @Override
    public void setSize(int width, int height) {

        int adjustedWidth = getAdjustedWidth();
        super.setSize(adjustedWidth, height);
    }

    /**
     * Method description
     *
     * @param d
     */
    @Override
    public void setSize(Dimension d) {

        int adjustedWidth = getAdjustedWidth();
        super.setSize(new Dimension(adjustedWidth, d.height));
    }

    /**
     * Calculate a new X location for this component
     *
     * @return The new X location.
     */
    private int getAdjustedX() {

        int adjustedX = 0;
        Integer attributePanelEnd = null;
        int width = 0;

        // Pack the attribute panels
        Component[] children = getParent().getComponents();
        for (final Component child : children) {
            if (child instanceof Packable) {

                if (attributePanelEnd == null) {
                    int x = child.getX();
                    adjustedX = x;
                    width = child.getWidth();
                    attributePanelEnd = new Integer(x + width);
                }
                break;
            }
        }

        // New X location
        if (attributePanelEnd != null) {
            adjustedX = attributePanelEnd.intValue();

            if (width > 0) {
                adjustedX += IGVPanel.X_SPACING_BETWEEN_COMPONENTS;
            }
        }

        return adjustedX;
    }

    /**
     * Calculate a new width location for this component
     *
     * @return The new width.
     */
    private int getAdjustedWidth() {

        int width = getWidth();

        Container parent = (Container) getParent();
        if (parent != null) {
            int viewportWidth = ((TrackPanel) parent).getViewportWidth();

            int parentVisibleWidth = ((JPanel) parent).getVisibleRect().width;
            if (parentVisibleWidth < viewportWidth) {
                viewportWidth = parentVisibleWidth;
            }

            int delta = viewportWidth - (getX() + width);
            width = getWidth() + delta;
        }

        // Width cannot be less than minimum width
        int minimumWidth = getMinimumSize().width;
        if (width < minimumWidth) {
            width = minimumWidth;
        }

        return width;
    }

    /**
     * @return
     */
    public Collection<TrackGroup> getTrackGroups() {
        TrackPanel dataTrackView = (TrackPanel) getParent();
        return dataTrackView.getGroups();
    }

    /**
     * Method description
     *
     * @return
     */
    public int getVisibleHeight() {
        TrackPanel dataTrackView = (TrackPanel) getParent();
        return dataTrackView.getVisibleRect().height;
    }


    /**
     * Some performance hings from the Mac developer mailing list.  Might improve
     * graphics performance?
     * <p/>
     * // TODO  do timing tests with and without these hints
     *
     * @param g2D
     */
    private void applyMacPerformanceHints(Graphics2D g2D) {

        // From mac mailint list.  Might help performance ?
        g2D.setRenderingHint(RenderingHints.KEY_ALPHA_INTERPOLATION, RenderingHints.VALUE_ALPHA_INTERPOLATION_SPEED);
        g2D.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_OFF);
        g2D.setRenderingHint(RenderingHints.KEY_COLOR_RENDERING, RenderingHints.VALUE_COLOR_RENDER_SPEED);
        g2D.setRenderingHint(RenderingHints.KEY_DITHERING, RenderingHints.VALUE_DITHER_DISABLE);
        g2D.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR);
        g2D.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_SPEED);


    }

    class DataPanelMouseAdapter extends MouseInputAdapter {

        // TODO -- a static member on the frame class is probably not
        // the best place for this.

        @Override
        public void mouseEntered(MouseEvent e) {
        }

        @Override
        public void mouseExited(MouseEvent e) {
            IGVMainFrame.getInstance().setSelectedRegion(null);
        }

        /**
         * The mouse has been clicked.  If the mode is ZOOM_IN or ZOOM_OUT
         * zoom and center on click.  Otherwise ignore
         */
        @Override
        public void mousePressed(final MouseEvent e) {

            if (SwingUtilities.getWindowAncestor(DataPanel.this).isActive()) {
                DataPanel.this.requestFocus();
                //DataPanel.this.grabFocus();
            }

            if (e.isPopupTrigger()) {
                IGVMainFrame.getInstance().getTrackManager().clearSelections();
                selectTracks(e);
                openPopupMenu(e);
            }
        }

        @Override
        public void mouseMoved(MouseEvent e) {
            tooltipTextPosition = e.getPoint();

        }

        @Override
        public void mouseReleased(MouseEvent e) {

            if (e.isPopupTrigger()) {
                IGVMainFrame.getInstance().getTrackManager().clearSelections();
                selectTracks(e);
                openPopupMenu(e);
            }

        }
    }

    private class FileDropTargetListener implements DropTargetListener {

        private TrackPanel panel;

        public FileDropTargetListener(TrackPanel dataPanel) {
            panel = dataPanel;
        }

        public void dragEnter(DropTargetDragEvent event) {

            if (!isDragAcceptable(event)) {
                event.rejectDrag();
                return;
            }
        }

        public void dragExit(DropTargetEvent event) {
        }

        public void dragOver(DropTargetDragEvent event) {
            // you can provide visual feedback here
        }

        public void dropActionChanged(DropTargetDragEvent event) {
            if (!isDragAcceptable(event)) {
                event.rejectDrag();
                return;
            }
        }

        public void drop(DropTargetDropEvent event) {
            if (!isDropAcceptable(event)) {
                event.rejectDrop();
                return;
            }

            event.acceptDrop(DnDConstants.ACTION_COPY);

            Transferable transferable = event.getTransferable();

            DataFlavor[] flavors = transferable.getTransferDataFlavors();

            MessageCollection messages = new MessageCollection();
            try {

                String obj = transferable.getTransferData(DataFlavor.stringFlavor).toString();
                if (obj.startsWith("http:")) {
                      IGVMainFrame.getInstance().loadTracks(Arrays.asList(new ResourceLocator(obj)));
                } else {
                    List<File> files = (List<File>) transferable.getTransferData(DataFlavor.javaFileListFlavor);

                    for (File file : files) {
                        try {

                            IGVMainFrame.getInstance().getTrackManager().load(file, panel);
                        }
                        catch (DataLoadException de) {
                            messages.append(de.getMessage());
                        }
                    }
                }

                if (messages != null && !messages.isEmpty()) {
                    MessageUtils.showAndLogErrorMessage(IGVMainFrame.getInstance(), messages.getFormattedMessage(), log);
                }
            }
            catch (Exception e) {
                log.error(e.getMessage(), e);
            }
            IGVMainFrame.getInstance().repaint();
            event.dropComplete(true);
        }

        public boolean isDragAcceptable(DropTargetDragEvent event) { // usually, you
            // check the
            // available
            // data flavors
            // here
            // in this program, we accept all flavors
            return (event.getDropAction() & DnDConstants.ACTION_COPY_OR_MOVE) != 0;
        }

        public boolean isDropAcceptable(DropTargetDropEvent event) { // usually, you
            // check the
            // available
            // data flavors
            // here
            // in this program, we accept all flavors
            return (event.getDropAction() & DnDConstants.ACTION_COPY_OR_MOVE) != 0;
        }
    }
}
