DragTabFrame fechando inconsistentemente

O código abaixo deve funcionar um pouco como a interface de vários documentos (MDI) que você pode ver em um navegador como FF, IE ou Chrome. Ele apresenta 'documentos' (uma imagem em buffer preta como espaçador) em um painel com guias, para que possam ser arrastados do painel para uma nova janela (ou existente) por opção do usuário.

Mas ele teve problemas com o fechamento de quadros, uma vez que eles não têm mais guias, além de fechar a JVM quando não há outras janelas visíveis. Eupensar Eu corrigi-los, verificando com umTimer noDragTabManager:

Ele verifica quadros abertos para instâncias deDragTabFrameSe encontrar um, verifique a contagem de guias. Se 0 o quadro estiver definido como invisível e descartado.Se não encontrar nenhuma instância do quadro visível, ele terminaráTimer para permitir que o JRE saia.

Pelo menos é assim que deve funcionar. Parece estar funcionando de forma confiável aqui, e não vi nenhum 'quadro vazio ou VM não desligando' para alguns testes. Funciona como anunciado para outras pessoas ou preciso procurar mais?

import java.awt.*;
import java.awt.event.*;
import java.awt.image.BufferedImage;
import javax.swing.*;
import javax.swing.border.EmptyBorder;

public class DragTabFrame extends JFrame {

    private JTabbedPane tabbedPane = new JTabbedPane();
    private final static DragTabManager dragTabManager = new DragTabManager();

    final MouseAdapter ma = new MouseAdapter() {

        @Override
        public void mouseClicked(MouseEvent e) {
            JComponent c = (JComponent) e.getSource();
            dragTabManager.setCurrentComponent(c);
            DragTabFrame dtf = (DragTabFrame)c.getTopLevelAncestor();
            dragTabManager.setCurrentFrame(dtf);
            JTabbedPane tp = dtf.getTabbedPane();
            int index = tp.indexOfComponent(c);
            if (index<0) index = 0;
            String title = tp.getTitleAt(index);
            dragTabManager.setCurrentTitle(title);
        }

        @Override
        public void mousePressed(MouseEvent e) {
            JComponent c = (JComponent) e.getSource();
            DragTabFrame dtf = (DragTabFrame)c.getTopLevelAncestor();
            dragTabManager.setCurrentComponent(c);
            dragTabManager.setCurrentFrame(dtf);
            JTabbedPane tp = dtf.getTabbedPane();
            int index = tp.indexOfComponent(c);
            if (index<0) index = 0;
            String title = tp.getTitleAt(index);
            dragTabManager.setCurrentTitle(title);
        }

        @Override
        public void mouseReleased(MouseEvent e) {
            JComponent c = (JComponent) e.getSource();
            if (c.getTopLevelAncestor().getBounds().contains(e.getLocationOnScreen())) {
                // do nothing, the drop point is the same frame
            } else {
                DragTabFrame dtf = getTargetFrame(e.getLocationOnScreen());
                if (dtf == null) {
                    dtf = new DragTabFrame();
                    dtf.init();
                    dtf.setLocation(e.getLocationOnScreen());
                } else {
                    DragTabFrame fromFrame = dragTabManager.getCurrentFrame();
                    fromFrame.removeTabComponent(c);
                    JTabbedPane tp = fromFrame.getTabbedPane();
                    if (tp.getTabCount() == 0) {
                        fromFrame.setVisible(false);
                        fromFrame.dispose();
                    } 
                }
                dtf.addTabComponent(dragTabManager.getCurrentTitle(), c);
                dtf.pack();
                dtf.setVisible(true);
            }
        }
    };

    public JTabbedPane getTabbedPane() {
        return tabbedPane;
    }

    public DragTabFrame getTargetFrame(Point p) {
        Frame[] frames = Frame.getFrames();
        for (Frame frame : frames) {
            if (frame instanceof DragTabFrame
                    && frame.getBounds().contains(p)) {
                return (DragTabFrame) frame;
            }
        }

        return null;
    }

    public void init() {
        // the GUI as seen by the user (without frame)
        JPanel gui = new JPanel(new BorderLayout());
        gui.setBorder(new EmptyBorder(2, 3, 2, 3));
        gui.add(tabbedPane, BorderLayout.CENTER);
        add(gui);

        setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
        // See https://stackoverflow.com/a/7143398/418556 for demo.
        setLocationByPlatform(true);
    }

    public void addTabComponent(String name, Component c) {
        tabbedPane.addTab(name, c);
        c.addMouseListener(ma);
        c.addMouseMotionListener(ma);
    }

    public void removeTabComponent(Component c) {
        tabbedPane.remove(c);
        c.removeMouseListener(ma);
        c.removeMouseMotionListener(ma);
    }
    public static void main(String[] args) {
        Runnable r = new Runnable() {

            @Override
            public void run() {

                DragTabFrame dtf = new DragTabFrame();
                dtf.init();
                BufferedImage bi = new BufferedImage(
                        200, 40, BufferedImage.TYPE_INT_RGB);
                for (int ii = 1; ii < 4; ii++) {
                    JLabel l = new JLabel(new ImageIcon(bi));
                    dtf.addTabComponent("Tab " + ii, l);
                }
                dtf.pack();
                // should be done last, to avoid flickering, moving,
                // resizing artifacts.
                dtf.setVisible(true);
            }
        };
        // Swing GUIs should be created and updated on the EDT
        // http://docs.oracle.com/javase/tutorial/uiswing/concurrency/initial.html
        SwingUtilities.invokeLater(r);
    }
}

class DragTabManager {
    private DragTabFrame currentFrame;
    private JComponent currentComponent;
    private String currentTitle;
    private Timer timer;

    public DragTabManager() {
        ActionListener actionListener = new ActionListener() {

            @Override
            public void actionPerformed(ActionEvent e) {
                Frame[] frames = Frame.getFrames();
                if (frames.length==0) {
                    timer.stop();
                }
                System.out.println("frames.length: " + frames.length);
                boolean allInvisible = true;
                for (Frame frame : frames) {
                    if (frame instanceof DragTabFrame) {
                        DragTabFrame dtf = (DragTabFrame)frame;
                        if (dtf.isVisible()) {
                            allInvisible = false;
                        }
                        if (dtf.getTabbedPane().getTabCount()==0) {
                            dtf.setVisible(false);
                            dtf.dispose();
                        }
                    }
                }
                if (allInvisible) {
                    timer.stop();
                }
            }
        };
        timer = new Timer(200,actionListener);
        timer.start();
    }

    /**
     * @return the currentFrame
     */
    public DragTabFrame getCurrentFrame() {
        return currentFrame;
    }

    /**
     * @param currentFrame the currentFrame to set
     */
    public void setCurrentFrame(DragTabFrame currentFrame) {
        this.currentFrame = currentFrame;
    }

    /**
     * @return the currentComponent
     */
    public JComponent getCurrentComponent() {
        return currentComponent;
    }

    /**
     * @param currentComponent the currentComponent to set
     */
    public void setCurrentComponent(JComponent currentComponent) {
        this.currentComponent = currentComponent;
    }

    /**
     * @return the currentTitle
     */
    public String getCurrentTitle() {
        return currentTitle;
    }

    /**
     * @param currentTitle the currentTitle to set
     */
    public void setCurrentTitle(String currentTitle) {
        this.currentTitle = currentTitle;
    }
}
Consertar

De acordo com as sugestões doresposta do DSquare, aqui está a fonte fixa, com alguns outros ajustes também.

import java.awt.*;
import java.awt.event.*;
import java.awt.image.BufferedImage;
import javax.swing.*;
import javax.swing.border.EmptyBorder;

public class DragTabFrame extends JFrame {

    private JTabbedPane tabbedPane = new JTabbedPane();
    private final static DragTabManager dragTabManager = new DragTabManager();
    final MouseAdapter ma = new MouseAdapter() {

        @Override
        public void mousePressed(MouseEvent e) {
            JComponent c = (JComponent) e.getSource();
            DragTabFrame dtf = (DragTabFrame) c.getTopLevelAncestor();
            dragTabManager.setCurrentComponent(c);
            dragTabManager.setCurrentFrame(dtf);
            JTabbedPane tp = dtf.getTabbedPane();
            int index = tp.indexOfComponent(c);
            String title = tp.getTitleAt(index);
            dragTabManager.setCurrentTitle(title);
        }

        @Override
        public void mouseReleased(MouseEvent e) {
            JComponent c = (JComponent) e.getSource();
            if (c.getTopLevelAncestor().getBounds().contains(
                    e.getLocationOnScreen())) {
                // do nothing, the drop point is the same frame
            } else {
                DragTabFrame dtf = getTargetFrame(e.getLocationOnScreen());
                if (dtf == null) {
                    dtf = new DragTabFrame();
                    dtf.init();
                    dtf.setLocation(e.getLocationOnScreen());
                }
                DragTabFrame fromFrame = dragTabManager.getCurrentFrame();
                fromFrame.removeTabComponent(c);
                JTabbedPane tp = fromFrame.getTabbedPane();
                if (tp.getTabCount() == 0) {
                    fromFrame.setVisible(false);
                    fromFrame.dispose();
                }
                dtf.addTabComponent(dragTabManager.getCurrentTitle(), c);
                dtf.pack();
                dtf.setVisible(true);
            }
        }
    };

    public JTabbedPane getTabbedPane() {
        return tabbedPane;
    }

    public DragTabFrame getTargetFrame(Point p) {
        Frame[] frames = Frame.getFrames();
        for (Frame frame : frames) {
            if (frame instanceof DragTabFrame
                    && frame.getBounds().contains(p)) {
                return (DragTabFrame) frame;
            }
        }

        return null;
    }

    public void init() {
        // the GUI as seen by the user (without frame)
        JPanel gui = new JPanel(new BorderLayout());
        gui.setBorder(new EmptyBorder(2, 3, 2, 3));
        gui.add(tabbedPane, BorderLayout.CENTER);
        add(gui);

        setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
        // See https://stackoverflow.com/a/7143398/418556 for demo.
        setLocationByPlatform(true);
    }

    public void addTabComponent(String name, Component c) {
        tabbedPane.addTab(name, c);
        c.addMouseListener(ma);
        c.addMouseMotionListener(ma);
    }

    public void removeTabComponent(Component c) {
        tabbedPane.remove(c);
        c.removeMouseListener(ma);
        c.removeMouseMotionListener(ma);
    }

    public static void main(String[] args) {
        Runnable r = new Runnable() {

            @Override
            public void run() {

                DragTabFrame dtf = new DragTabFrame();
                dtf.init();
                BufferedImage bi = new BufferedImage(
                        200, 40, BufferedImage.TYPE_INT_RGB);
                for (int ii = 1; ii < 4; ii++) {
                    JLabel l = new JLabel(new ImageIcon(bi));
                    dtf.addTabComponent("Tab " + ii, l);
                }
                dtf.pack();
                dtf.setVisible(true);
            }
        };
        SwingUtilities.invokeLater(r);
    }
}

class DragTabManager {

    private DragTabFrame currentFrame;
    private JComponent currentComponent;
    private String currentTitle;

    /**
     * @return the currentFrame
     */
    public DragTabFrame getCurrentFrame() {
        return currentFrame;
    }

    /**
     * @param currentFrame the currentFrame to set
     */
    public void setCurrentFrame(DragTabFrame currentFrame) {
        this.currentFrame = currentFrame;
    }

    /**
     * @return the currentComponent
     */
    public JComponent getCurrentComponent() {
        return currentComponent;
    }

    /**
     * @param currentComponent the currentComponent to set
     */
    public void setCurrentComponent(JComponent currentComponent) {
        this.currentComponent = currentComponent;
    }

    /**
     * @return the currentTitle
     */
    public String getCurrentTitle() {
        return currentTitle;
    }

    /**
     * @param currentTitle the currentTitle to set
     */
    public void setCurrentTitle(String currentTitle) {
        this.currentTitle = currentTitle;
    }
}

questionAnswers(1)

yourAnswerToTheQuestion