Tuesday, 3 September 2013

DragTabFrame closing inconsistently

DragTabFrame closing inconsistently

The code below is supposed to work a little like the Multi-Document
Interface (MDI) you might see in a browser like FF, IE or Chrome. It
presents 'documents' (a black buffered image as spacer) in a tabbed pane
such that they can be dragged from the pane into a new (or existing)
window by user choice.
But it has had issues with closing frames once they have no more tabs, as
well as closing the JVM when there are no further visible windows. I think
I fixed them by checking with a Timer in the DragTabManager:
It checks open frames for instances of DragTabFrame
If it finds one, it check the tab count. If 0 the frame is set invisible
and disposed.
If it finds no instances of the frame that are visible, it ends the Timer
to allow the JRE to exit.
At least that is how it is supposed to work. It seems to be working
reliably here, and I have seen no 'empty frame or VM failing to shut down'
for quite a few tests. Does it work as advertised for others, or do I need
to look further?
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 http://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;
}
}

No comments:

Post a Comment