/*
!=======================================================================
!
!  PROGRAM  PHASE-Viewer  (PHASE-Viewer 2014.01 ver.3.3.0)
!
!  Created on 2005/08/16, 10:52
!  AUTHOR(S): KOGA, Junichiro
!  File : TabbedProjectManipulator.java
!  
!  Contact address :  Phase System Consortium
!                     E-mail: phase_system@nims.go.jp URL https://azuma.nims.go.jp
!
!
!   Since 2002, this program set had been intensively developed as a part of the following 
!  national projects supported by the Ministry of Education, Culture, Sports, Science and
!  Technology (MEXT) of Japan; "Frontier Simulation Software for Industrial Science
!  (FSIS)" from 2002 to 2005, "Revolutionary Simulation Software (RSS21)" from 2006 to
!  2008. "Research and Development of Innovative Simulation Software (RISS)" from 2008
!  to 2013. These projects is lead by the Center for Research on Innovative Simulation 
!  Software (CISS), the Institute of Industrial Science (IIS), the University of Tokyo.
!   Since 2013, this program set has been further developed centering on PHASE System
!  Consortium. 
!   The activity of development of this program set has been supervised by Takahisa Ohno.
!
!=======================================================================
 */

package ciss.phase_viewer.projectbrowser;

import java.awt.AlphaComposite;
import java.awt.Color;
import java.awt.Component;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridLayout;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.Transferable;
import java.awt.dnd.DnDConstants;
import java.awt.dnd.DragGestureEvent;
import java.awt.dnd.DragGestureListener;
import java.awt.dnd.DragSource;
import java.awt.dnd.DragSourceDragEvent;
import java.awt.dnd.DragSourceDropEvent;
import java.awt.dnd.DragSourceEvent;
import java.awt.dnd.DragSourceListener;
import java.awt.dnd.DropTarget;
import java.awt.dnd.DropTargetDragEvent;
import java.awt.dnd.DropTargetDropEvent;
import java.awt.dnd.DropTargetEvent;
import java.awt.dnd.DropTargetListener;
import java.awt.dnd.InvalidDnDOperationException;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
import java.io.File;
import java.net.URL;
import java.util.HashMap;
import java.util.Vector;

import javax.swing.BoxLayout;
import javax.swing.ImageIcon;
import javax.swing.JCheckBoxMenuItem;
import javax.swing.JComponent;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.JScrollPane;
import javax.swing.JTabbedPane;
import javax.swing.SwingUtilities;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;

import org.apache.log4j.Logger;
import org.jdom.Document;
import org.jdom.Element;

import ciss.phase_viewer.common.MyJCheckBoxMenuItem;
import ciss.phase_viewer.jdom.MyElement;
import ciss.phase_viewer.jdom.XMLUtils;
import ciss.phase_viewer.main.PluginLoader;
import ciss.phase_viewer.scripting.ScriptUtils;

/**
 * Extensible jtabbed pane.
 * 
 * @author KOGA, Junichiro
 */
// public class TabbedProjectManipulator extends JTabbedPane {
public class TabbedProjectManipulator extends JPanel {
    private static Logger logger = Logger
            .getLogger(TabbedProjectManipulator.class.getName());

    private URL property;

    private ProjectInfo info;

    private String scriptName = null;

    private Vector reloadListeners;

    private JTabbedPane tabbedPane;

    private boolean dndSupport = true;

    /**
     * Creates a new instance of TabbedProjectManipulator.
     * 
     * @param property
     *            XML file to specify what tab to add.
     * @param info
     *            a "ProjectInfo" object, which encapsulates information
     *            regarding the target project.
     */
    public TabbedProjectManipulator(URL property, ProjectInfo info) {
        super();
        this.info = info;
        this.property = property;
        this.init();
    }

    public TabbedProjectManipulator(URL property, ProjectInfo info,
            boolean dndSupport) {
        super();
        this.info = info;
        this.property = property;
        this.dndSupport = dndSupport;
        this.init();
    }

    /**
     * このGUIに関連づけられたJTabbedPaneに指定のコンポーネントを足す
     * 
     * @param component
     *            足したいコンポーネント
     * @param index
     *            足したいindex
     */
    public void addToTabbedPane(JComponent component, int index) {
        tabbedPane.add(component, index);
    }

    private void init() {
        // if ( dndSupport )
        // this.tabbedPane = new DnDTabbedPane(property.getPath());
        // else
        // this.tabbedPane = new JTabbedPane();

        this.tabbedPane = new JTabbedPane();

        this.setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
        this.add(tabbedPane);
        // this.property = property;
        // this.info = info;
        parseAndAddTabs();

        revalidate();
    }

    public void addReloadListener(ReloadListener rlistener) {
        if (reloadListeners == null)
            reloadListeners = new Vector();
        reloadListeners.add(rlistener);
        logger.debug(rlistener);
    }

    public void removeReloadListener(ReloadListener rlistener) {
        if (reloadListeners == null)
            return;
        reloadListeners.remove(rlistener);
    }

    /**
     * SubSelectorのボタンクリックを通知してもらうためのメソッド.
     */
    public void buttonPressed() {
        int count = tabbedPane.getComponentCount();
        for (int i = 0; i < count; i++) {
            if (tabbedPane.getComponent(i) instanceof TabPanel) {
                ((TabPanel) tabbedPane.getComponent(i)).btnPressed();
            }
        }
    }

    private void parseAndAddTabs() {
        Document doc = XMLUtils.getDocumentFromURL(property);
        if (doc == null) {
            logger.error("couldn't get document from " + property);
            return;
        }

        Element elem = doc.getRootElement();
        if (!elem.getName().equals("root")) {
            logger.error("invalid ProjectSpecification file.");
            return;
        }

        Element elem_tab = elem.getChild("tabbed_pane_properties");
        boolean wrap = new Boolean(elem_tab.getChildTextTrim("wrap"))
                .booleanValue();
        if (wrap) {
            tabbedPane.setTabLayoutPolicy(JTabbedPane.WRAP_TAB_LAYOUT);
        }

        String layout = elem_tab.getChildTextTrim("tab_layout");
        if (layout != null) {
            if (layout.equalsIgnoreCase("wrap")) {
                tabbedPane.setTabLayoutPolicy(JTabbedPane.WRAP_TAB_LAYOUT);
            } else if (layout.equalsIgnoreCase("scroll")) {
                tabbedPane.setTabLayoutPolicy(JTabbedPane.SCROLL_TAB_LAYOUT);
            }
        }

        String placement = elem_tab.getChildTextTrim("tab_placement");
        if (placement != null) {
            if (placement.equalsIgnoreCase("top")) {
                tabbedPane.setTabPlacement(JTabbedPane.TOP);
            } else if (placement.equalsIgnoreCase("left")) {
                tabbedPane.setTabPlacement(JTabbedPane.LEFT);
            } else if (placement.equalsIgnoreCase("bottom")) {
                tabbedPane.setTabPlacement(JTabbedPane.BOTTOM);
            } else if (placement.equalsIgnoreCase("right")) {
                tabbedPane.setTabPlacement(JTabbedPane.RIGHT);
            }
        }

        Element elem_tabs = elem.getChild("tabs");
        java.util.List tab_list = elem_tabs.getChildren();
        if (tab_list == null || tab_list.size() == 0) {
            logger.warn("no tab to add...");
            return;
        }

        for (int i = 0; i < tab_list.size(); i++) {
            Element element = (Element) tab_list.get(i);
            addManipulator(element);
        }

        Element scrname = elem_tab.getChild("scriptname");
        if (scrname != null)
            scriptName = info.getProjectDirectory()
                    + System.getProperty("file.separator")
                    + scrname.getTextTrim();

        if (scriptName != null && new File(scriptName).exists())
            runScript();

        tabbedPane.addChangeListener(new ChangeListener() {
            public void stateChanged(ChangeEvent ce) {
                updateTab();
            }
        });

        tabbedPane.addMouseListener(new MouseListener() {

            public void mouseClicked(MouseEvent e) {
                // TODO Auto-generated method stub

            }

            public void mouseEntered(MouseEvent e) {
                // TODO Auto-generated method stub

            }

            public void mouseExited(MouseEvent e) {
                // TODO Auto-generated method stub

            }

            public void mousePressed(MouseEvent e) {
                // TODO Auto-generated method stub
                if (tabbedPane instanceof DnDTabbedPane)
                    ((DnDTabbedPane) tabbedPane).setGlassPane();
            }

            public void mouseReleased(MouseEvent e) {
                // TODO Auto-generated method stub
            }

        });

        tabbedPane.addMouseListener(new MouseListenerTab());
        // tabbedPane.setSelectedIndex(0);
        updateTab();
        // tabbedPane.fireStateChanged();
    }

    private void updateTab() {
        Component comp = tabbedPane.getSelectedComponent();
        if (comp == null)
            return;

        if (reloadListeners != null)
            for (int i = 0; i < reloadListeners.size(); i++) {
                Object ob = reloadListeners.get(i);
                if (ob == null)
                    continue;
                JComponent jc = ((ReloadListener) ob).getReloadingComponent();
                if (jc == null)
                    continue;
                if (comp == jc) {
                    ((ReloadListener) ob).reload();
                }
            }
    }

    private void runScript() {
        HashMap props = new HashMap();
        props.put("parent", this);
        props.put("projectInfo", info);
        ScriptUtils.runScript(scriptName, props);
        logger.debug("run script: " + scriptName);
    }

    class MouseListenerTab extends MouseAdapter {
        public void mouseClicked(MouseEvent e) {
            if (e.getButton() == MouseEvent.BUTTON3) {
                showMenu(e);
            }
        }
    }

    private void showMenu(MouseEvent e) {
        JPopupMenu popup = new JPopupMenu();
        for (int i = 0; i < tabPanels.size(); i++) {
            TabPanel panel = (TabPanel) tabPanels.elementAt(i);
            MyJCheckBoxMenuItem item = new MyJCheckBoxMenuItem(panel.toString());
            item.setState(panel.getShowTab());
            item.addActionListener(new ActionListenerPopup(panel));
            popup.add(item);
        }
        popup.show(e.getComponent(), e.getX(), e.getY());
    }

    class ActionListenerPopup implements ActionListener {
        private TabPanel tabPanel;

        ActionListenerPopup(TabPanel tabPanel) {
            this.tabPanel = tabPanel;
        }

        public void actionPerformed(ActionEvent ae) {
            JCheckBoxMenuItem mi = (JCheckBoxMenuItem) ae.getSource();
            if (!tabPanel.getShowTab()) {
                tabbedPane.addTab(tabPanel.toString(), tabPanel);
                String iconpath = tabPanel.getIconPath();
                String description = tabPanel.getDescription();
                if (iconpath != null && iconpath.length() != 0) {
                    URL res = null;
                    try {
                        PluginLoader loader = new PluginLoader();
                        res = loader.getResource(iconpath);
                        tabbedPane.setIconAt(tabbedPane.getTabCount() - 1,
                                new ImageIcon(res));
                    } catch (Exception ex) {
                        logger.error("failed to resolve icon path.");
                    }
                }

                if (description != null && description.length() != 0) {
                    tabbedPane.setToolTipTextAt(tabbedPane.getTabCount() - 1,
                            description);
                }
                tabPanel.setShowTab(true);
            } else {
                tabbedPane.remove(tabPanel);
                tabPanel.setShowTab(false);
            }
            if (tabbedPane.getTabCount() != 0) {
                tabbedPane.setSelectedIndex(0);
                updateTab();
                // fireStateChanged();
            }
        }
    }

    public void reload() {
        if (reloadListeners != null)
            for (int i = 0; i < reloadListeners.size(); i++)
                ((ReloadListener) reloadListeners.get(i)).setReload(true);
        tabbedPane.setSelectedIndex(tabbedPane.getSelectedIndex());
        // fireStateChanged();
        updateTab();
    }

    private Vector tabPanels = new Vector();

    private void addManipulator(Element element) {
        String name = element.getChildTextTrim("name");
        String iconpath = element.getChildTextTrim("iconpath");
        String loadClass = element.getChildTextTrim("loadclass");
        String description = MyElement.decode(element
                .getChildTextTrim("description"));
        String init_visible = MyElement.decode(element
                .getChildTextTrim("init_visible"));
        boolean binit_visible = true;
        if (init_visible != null && init_visible.equalsIgnoreCase("false")) {
            binit_visible = false;
        }

        logger.debug("adding " + name);
        TabPanel tabPanel = new TabPanel(this, name, loadClass, info,
                description, iconpath);
        addReloadListener(tabPanel);
        tabPanels.addElement(tabPanel);

        if (binit_visible) {
            tabbedPane.addTab(name, tabPanel);
            PluginLoader loader = new PluginLoader();
            if (iconpath != null && iconpath.length() != 0
                    && !iconpath.equals("null")) {
                URL res = null;
                try {
                    res = loader.getResource(iconpath);
                    tabbedPane.setIconAt(tabbedPane.getTabCount() - 1,
                            new ImageIcon(res));
                } catch (Exception ex) {
                    logger.error("failed to resolve icon path.");
                }
            }

            if (description != null && description.length() != 0) {
                tabbedPane.setToolTipTextAt(tabbedPane.getTabCount() - 1,
                        description);
            }
        } else {
            tabPanel.setShowTab(false);
        }
    }

    class TabPanel extends JPanel implements ReloadListener {
        private Logger logger = Logger.getLogger(TabPanel.class.getName());

        private String loadClass;

        private ProjectInfo info;

        private ProjectManipulator manip = null;

        private TabbedProjectManipulator parent;

        private boolean reload = false;

        private String name;

        private boolean showTab = true;

        private String description;

        private String iconpath;

        protected TabPanel(TabbedProjectManipulator parent, String name,
                String loadClass, ProjectInfo info, String description,
                String iconpath) {
            this.parent = parent;
            this.name = name;
            this.loadClass = loadClass;
            this.info = info;
            this.description = description;
            this.iconpath = iconpath;
            setLayout(new GridLayout());
        }

        protected void btnPressed() {
            if (manip != null)
                manip.buttonPressed();
        }

        protected String getDescription() {
            return this.description;
        }

        protected String getIconPath() {
            return this.iconpath;
        }

        public void reload() {
            if (manip == null || reload) {
                manip = (ProjectManipulator) PluginLoader.instantiate(
                        loadClass, new Object[] { info });
                if (manip != null) {
                    // System.out.println("init");
                    // System.out.println("class: "+manip.getClass().getName());
                    manip.init();
                    info.getParent().registerProjectManipulators(manip);
                    logger.debug("adding " + loadClass);
                    removeAll();
                    boolean in_scr = false;
                    Component parentComp = this.getParent();
                    // while( true ) {
                    // if ( parentComp == null ) {
                    // break;
                    // }
                    if (parentComp instanceof JScrollPane) {
                        in_scr = true;
                        // break;
                    }
                    // parentComp = parentComp.getParent();
                    // }
                    if (!in_scr) {
                        JScrollPane pane = new JScrollPane(manip);
                        manip.setPreferredSize(pane.getPreferredSize());
                        add(pane);
                    } else {
                        add(manip);
                    }
                    manip.postInit();
                }
            }
            reload = false;
        }

        protected void setShowTab(boolean showTab) {
            this.showTab = showTab;
        }

        protected boolean getShowTab() {
            return this.showTab;
        }

        public void setReload(boolean reload) {
            this.reload = reload;
        }

        public JComponent getReloadingComponent() {
            return this;
        }

        public String toString() {
            return name;
        }

    }

    private Component origGlassPane;

    class DnDTabbedPane extends JTabbedPane {
        private static final int LINEWIDTH = 3;

        private String NAME = "test";

        private GhostGlassPane glassPane = new GhostGlassPane();

        private Rectangle2D lineRect = new Rectangle2D.Double();

        private Color lineColor = new Color(0, 100, 255);

        private DragSource dragSource = new DragSource();

        private DropTarget dropTarget;

        private int dragTabIndex = -1;

        private DnDTabbedPane dndTabbedPane;

        public DnDTabbedPane(String name) {
            super();
            this.NAME = name;
            FLAVOR = new DataFlavor(DataFlavor.javaJVMLocalObjectMimeType, NAME);
            this.dndTabbedPane = this;
            DragGestureListenerImpl dgl = new DragGestureListenerImpl();
            dropTarget = new DropTarget(glassPane,
                    DnDConstants.ACTION_COPY_OR_MOVE,
                    new CDropTargetListener(), true);
            dragSource.createDefaultDragGestureRecognizer(this,
                    DnDConstants.ACTION_COPY_OR_MOVE, dgl);
            origGlassPane = info.getParent().getGlassPane();
            this.setGlassPane();
        }

        void setGlassPane() {
            info.getParent().setGlassPane(glassPane);
            // Desk.getDesktop().getSelectedFrame().setGlassPane(glassPane);
        }

        class DragGestureListenerImpl implements DragGestureListener {
            DragSourceListenerImpl dsl = new DragSourceListenerImpl();
            TransferableImpl t = new TransferableImpl();

            public void dragGestureRecognized(DragGestureEvent e) {
                initGlassPane(e.getComponent(), e.getDragOrigin());
                try {
                    e.startDrag(DragSource.DefaultMoveDrop, t, dsl);
                } catch (InvalidDnDOperationException idoe) {
                    idoe.printStackTrace();
                }
            }
        }

        class DragSourceListenerImpl implements DragSourceListener {
            public void dragEnter(DragSourceDragEvent e) {
                e.getDragSourceContext().setCursor(DragSource.DefaultMoveDrop);
            }

            public void dragExit(DragSourceEvent e) {
                e.getDragSourceContext()
                        .setCursor(DragSource.DefaultMoveNoDrop);
                lineRect.setRect(0, 0, 0, 0);
                glassPane.setPoint(new Point(-1000, -1000));
                glassPane.repaint();
            }

            public void dragOver(DragSourceDragEvent e) {
                Point p = (Point) e.getLocation().clone();
                SwingUtilities.convertPointFromScreen(p, DnDTabbedPane.this);
                // if(getTabAreaBound().contains(p) &&
                // dragTabIndex!=indexAtLocation(p.x, p.y)) {
                if (getTabAreaBound().contains(p)) {
                    e.getDragSourceContext().setCursor(
                            DragSource.DefaultMoveDrop);
                } else {
                    e.getDragSourceContext().setCursor(
                            DragSource.DefaultMoveNoDrop);
                }
            }

            public void dragDropEnd(DragSourceDropEvent e) {
                lineRect.setRect(0, 0, 0, 0);
                dragTabIndex = -1;
                if (hasGhost()) {
                    glassPane.setVisible(false);
                    glassPane.setImage(null);
                }
            }

            public void dropActionChanged(DragSourceDragEvent e) {
            }
        }

        private DataFlavor FLAVOR;

        class TransferableImpl implements Transferable {

            public Object getTransferData(DataFlavor flavor) {
                return dndTabbedPane;
            }

            public DataFlavor[] getTransferDataFlavors() {
                DataFlavor[] f = new DataFlavor[1];
                f[0] = FLAVOR;
                return f;
            }

            public boolean isDataFlavorSupported(DataFlavor flavor) {
                return (flavor.getHumanPresentableName().equals(NAME)) ? true
                        : false;
            }
        }

        class CDropTargetListener implements DropTargetListener {
            public void dragEnter(DropTargetDragEvent e) {

                if (isDragAcceptable(e))
                    e.acceptDrag(e.getDropAction());
                else {
                    e.rejectDrag();
                    // System.out.println("drag regected");
                }
            }

            public void dragExit(DropTargetEvent e) {
            }

            public void dropActionChanged(DropTargetDragEvent e) {
            }

            public void dragOver(final DropTargetDragEvent e) {
                initTargetLine(getTargetTabIndex(e.getLocation()));
                if (hasGhost()) {
                    glassPane.setPoint(e.getLocation());
                    glassPane.repaint();
                }
            }

            public void drop(DropTargetDropEvent e) {
                Transferable t = e.getTransferable();
                DataFlavor[] f = t.getTransferDataFlavors();
                if (isDropAcceptable(e)) {
                    convertTab(dragTabIndex, getTargetTabIndex(e.getLocation()));
                    e.dropComplete(true);
                } else {
                    e.dropComplete(false);
                }
                repaint();
            }

            public boolean isDragAcceptable(DropTargetDragEvent e) {
                Transferable t = e.getTransferable();
                if (t == null) {
                    // System.out.println("transferable is null");
                    return false;
                }
                DataFlavor[] f = e.getCurrentDataFlavors();
                if (t.isDataFlavorSupported(f[0]) && dragTabIndex >= 0) {
                    return true;
                }
                // System.out.println("data flavor supported : "+t.isDataFlavorSupported(e.getCurrentDataFlavors()[0])+" dragTabIndex: "+dragTabIndex);
                return false;
            }

            public boolean isDropAcceptable(DropTargetDropEvent e) {
                Transferable t = e.getTransferable();
                if (t == null)
                    return false;
                DataFlavor[] f = t.getTransferDataFlavors();
                Point p = (Point) e.getLocation().clone();
                if (t.isDataFlavorSupported(f[0]) && dragTabIndex >= 0
                        && dragTabIndex != indexAtLocation(p.x, p.y)) {
                    return true;
                }
                return false;
            }
        }

        private boolean hasghost = true;

        public void setPaintGhost(boolean flag) {
            hasghost = flag;
        }

        public boolean hasGhost() {
            return hasghost;
        }

        private int getTargetTabIndex(Point pt) {
            Point p = (Point) pt.clone();
            SwingUtilities.convertPointToScreen(p, glassPane);
            SwingUtilities.convertPointFromScreen(p, this);
            for (int i = 0; i < getTabCount(); i++) {
                Rectangle rect = getBoundsAt(i);
                rect.setRect(rect.x - rect.width / 2, rect.y, rect.width,
                        rect.height);
                if (rect.contains(p)) {
                    return i;
                }
            }
            Rectangle rect = getBoundsAt(getTabCount() - 1);
            rect.setRect(rect.x + rect.width / 2, rect.y, rect.width + 100,
                    rect.height);
            if (rect.contains(p)) {
                return getTabCount();
            } else {
                return -1;
            }
        }

        private void convertTab(int prev, int next) {
            if (next < 0 || prev == next) {
                // System.out.println("press="+prev+" next="+next);
                return;
            }
            Component cmp = getComponentAt(prev);
            String str = getTitleAt(prev);
            if (next == getTabCount()) {
                // System.out.println("last: press="+prev+" next="+next);
                remove(prev);
                addTab(str, cmp);
                setSelectedIndex(getTabCount() - 1);
            } else if (prev > next) {
                // System.out.println(" >: press="+prev+" next="+next);
                remove(prev);
                insertTab(str, null, cmp, null, next);
                setSelectedIndex(next);
            } else {
                // System.out.println(" <: press="+prev+" next="+next);
                remove(prev);
                insertTab(str, null, cmp, null, next - 1);
                setSelectedIndex(next - 1);
            }
        }

        private void initTargetLine(int next) {
            if (next < 0 || dragTabIndex == next || next - dragTabIndex == 1) {
                lineRect.setRect(0, 0, 0, 0);
            } else if (next == getTabCount()) {
                Rectangle rect = getBoundsAt(getTabCount() - 1);
                lineRect.setRect(rect.x + rect.width - LINEWIDTH / 2, rect.y,
                        LINEWIDTH, rect.height);
            } else if (next == 0) {
                Rectangle rect = getBoundsAt(0);
                lineRect.setRect(-LINEWIDTH / 2, rect.y, LINEWIDTH, rect.height);
            } else {
                Rectangle rect = getBoundsAt(next - 1);
                lineRect.setRect(rect.x + rect.width - LINEWIDTH / 2, rect.y,
                        LINEWIDTH, rect.height);
            }
            repaint();
        }

        private void initGlassPane(Component c, Point pt) {
            // if(!hasGhost()) return;
            Point p = (Point) pt.clone();
            dragTabIndex = indexAtLocation(p.x, p.y);
            // System.out.println("dragTabIndex: " + dragTabIndex);

            if (hasGhost()) {
                Rectangle rect = getBoundsAt(dragTabIndex);
                BufferedImage image = new BufferedImage(c.getWidth(),
                        c.getHeight(), BufferedImage.TYPE_INT_ARGB);
                Graphics g = image.getGraphics();
                c.paint(g);
                image = image.getSubimage(rect.x, rect.y, rect.width,
                        rect.height);
                glassPane.setImage(image);
            }
            glassPane.setVisible(true);
            SwingUtilities.convertPointToScreen(p, c);
            SwingUtilities.convertPointFromScreen(p, glassPane);
            glassPane.setPoint(p);
        }

        private Rectangle getTabAreaBound() {
            Rectangle lastTab = getUI().getTabBounds(this, getTabCount() - 1);
            return new Rectangle(0, 0, getWidth(), lastTab.y + lastTab.height);
        }

        public void paintComponent(Graphics g) {
            super.paintComponent(g);
            if (dragTabIndex >= 0) {
                Graphics2D g2 = (Graphics2D) g;
                g2.setPaint(lineColor);
                g2.fill(lineRect);
            }
        }
    }

    class GhostGlassPane extends JPanel {
        private final AlphaComposite composite;

        private Point location = new Point(0, 0);

        private BufferedImage dragged = null;

        public GhostGlassPane() {
            setOpaque(false);
            composite = AlphaComposite.getInstance(AlphaComposite.SRC_OVER,
                    0.5f);
        }

        public void setImage(BufferedImage dragged) {
            this.dragged = dragged;
        }

        public void setPoint(Point location) {
            this.location = location;
        }

        public void paintComponent(Graphics g) {
            if (dragged == null) {
                return;
            }
            Graphics2D g2 = (Graphics2D) g;
            g2.setComposite(composite);
            double xx = location.getX() - (dragged.getWidth(this) / 2);
            double yy = location.getY() - (dragged.getHeight(this) / 2);
            g2.drawImage(dragged, (int) xx, (int) yy, null);
        }

    }
}
