5

There is a JTable in my application with resizable header columns. Normally when I move the cursor over table header for resizing, the cursor icon changes to resize arrow, like <-->.

But things are different in the following scenario.

There is a button action in the same Frame, and during action performed, I am setting the cursor to busy icon and change it back to default cursor once the action is completed, using Container.setCursor(Cursor cursor) method.

Sometimes if I move the cursor over table header of resizing, after a button action, the cursor icon does not change to resize arrow, cursor does not change at all.

Can this be considered as a bug in Java Swing or is there a solution for this issue?

Update : Sample code attached

import java.util.*;  
import java.awt.*;  
import javax.swing.*;  
import java.awt.event.*;

public class ColumnResizeIconTest extends JFrame {

JScrollPane scrollPane;
JTable table;
JButton button;

public ColumnResizeIconTest() {
    setLayout(new BorderLayout());
    addComponents();
    setSize(300,300);
}

private void addComponents() {
    addButton();
    addTable();
}

private void addButton() {
    button = new JButton("Click Me");
    button.addActionListener( new ActionListener() {
        public void actionPerformed(ActionEvent ae) {
            setWaitCursor();
            for(int i=0; i<2000; i++) {
                System.out.print(i);
            }
            setDefaultCursor();
        }
    });
    add(button, BorderLayout.NORTH);
}

private void addTable() {
    scrollPane = new JScrollPane(createTable());
    add(scrollPane, BorderLayout.CENTER);
}

private JTable createTable() {
    Object[][] cellData = { { "1-1", "1-2","1-3" }, { "2-1", "2-2", "2-3" }, { "3-1", "3-2", "3-3" } };
    String[] columnNames = { "column1", "column2", "column3" };
    table = new JTable(cellData, columnNames);
    return table;
}

private void setWaitCursor() {
    Container container = getContentPane();
    setWaitCursor(container);
}

private void setWaitCursor(Container container) {
    for(int iCount = 0; iCount < container.getComponentCount(); iCount++) {
        Component child = (Component) container.getComponent(iCount);
        if(child instanceof Container) {
            setWaitCursor((Container) child);
        } else {
            child.setCursor(new Cursor(Cursor.WAIT_CURSOR));
        }
    }
    container.setCursor(new Cursor(Cursor.WAIT_CURSOR));
}

private void setDefaultCursor() {
    Container container = getContentPane();
    setDefaultCursor(container);
}

private void setDefaultCursor(Container container) {
    for(int iCount = 0; iCount < container.getComponentCount(); iCount++) {
        Component child = (Component) container.getComponent(iCount);
        if(child instanceof Container) {
            setDefaultCursor((Container) child);
        } else {
            child.setCursor(new Cursor(Cursor.DEFAULT_CURSOR));
        }
    }
    container.setCursor(new Cursor(Cursor.DEFAULT_CURSOR));
}

public static void main(String[] argv) throws Exception {
    ColumnResizeIconTest test = new ColumnResizeIconTest();
    test.setVisible(true);
}
}

Click on the button a few times and try to resize the table column. The cursor is stuck with Default cursor.

General Grievance
  • 4,555
  • 31
  • 31
  • 45
AnupD
  • 53
  • 1
  • 6
  • what version of java in what platform ? – ring bearer Jan 12 '12 at 05:44
  • 2
    1) For better help sooner, post an [SSCCE](http://sscce.org/). 2) *"Can this be considered as a bug.."* Probably. *"..in Java Swing.."* Probably not. *"..or is there a solution for this issue?"* Fix your code (probably). – Andrew Thompson Jan 12 '12 at 05:48
  • 1
    @Andrew Thompson how much probably for today, agreed with 4 probably from your 3. – mKorbel Jan 12 '12 at 06:18
  • @AnupD there talking about Action invoked from JButton ended with exceptions / Action still running, because you are starting long and hard events – mKorbel Jan 12 '12 at 06:22
  • 2
    no bug - most probably you are doing something wrong in re-/setting the cursor (there are three cursors involved, detecting which is the correct one to re-/set to is not entirely trivial) – kleopatra Jan 12 '12 at 07:17
  • @mKorbel ..I think I have a cache of 7 or 8 more probably's spare for today. Call me if you need some. ;) – Andrew Thompson Jan 12 '12 at 09:12
  • @Andrew Thompson sorry your cell is un_available, please can you call me byck at +00 000 000 000, please carrefully with international No. prefix, otherwise you could call by mistake to the heavens – mKorbel Jan 12 '12 at 09:45
  • @AndrewThompson Please excuse me for my mistakes as I am very new to this forum. I have attached the sample code. Please try it and see it yourself. I have screenshots but I do not know how to attach them here. – AnupD Jan 12 '12 at 10:20
  • @ringbearer Thanks for a supporting comment. I use jdk 1.6 u18 in win XP – AnupD Jan 12 '12 at 10:22
  • @mKorbel I am settign the wait sursor at starting of the action and changing it back to default cursor at the end of action. – AnupD Jan 12 '12 at 10:24
  • @kleopatra I hope it is something wrong with my code and I can fix it. I have attached the code now. Do yo have any idea what could be wtong here? – AnupD Jan 12 '12 at 10:25

2 Answers2

8

As already mentioned in my comment: it's not entirely trivial to re-/set the cursors, not even for a single component :-) The base problem (in the recursive cursor setting to wait) is the assumption that all components do have the default cursor.

As you see on the table header, that assumption is not correct: on that component, the "default" is either the defaultCursor or the resizeCursor, depending on mouse location. Additionally, the internal cursor toggling is not very intelligent: it doesn't check for state (from the top of my head, was hit by that fact a while ago :-)

Not entirely sure what you want to reach, so don't have a concrete solution, except dropping the recursive setting entirely, it's too hard to get right. Options might be

  • make the glassPane (of the frame's rootpane) visible and set the waitCursor on it
  • use JLayer (jdk7) or JXLayer (jdk6) on a smaller area and set the waitCursor on that
  • use a less intrusive visualization, f.i. JProgressBar or a JXBusyLabel (in the SwingX project) somewhere

Addendum (for @mKorbel :-)

the problem is easily reproducible, with a little change to the OPs SSCCE (thanks for that!): change the addButton method as below, then click on the button and while the wait cursor is shown, move the mouse into the header and then to another column (across the column border). Doing so several times will lead to unpredicable cursors on the header ...

private void addButton() {
    button = new JButton("Click Me");
    final ActionListener off = new ActionListener() {

        @Override
        public void actionPerformed(ActionEvent e) {
            setDefaultCursor();
            button.setEnabled(true);
        }

    };
    button.addActionListener( new ActionListener() {
        public void actionPerformed(ActionEvent ae) {
            setWaitCursor();
            button.setEnabled(false);
            Timer timer = new Timer(2000, off);
            timer.setRepeats(false);
            timer.start();
        }
    });

    add(button, BorderLayout.NORTH);
}
kleopatra
  • 51,061
  • 28
  • 99
  • 211
  • untill ..., I was sure that FRI 13th is tomorrow, if I remmember correctly this is EDT rellated issue, I read similair question (don't know where), about that, that this proces must be splited to two separated Actions, otherwise isn't guarantee orders of event for Objects that came from Native OS, maybe that same as with Focus – mKorbel Jan 12 '12 at 13:30
  • @mKorbel EDT violations often are the natural culprit - not this time, though, it's merely incomplete logic :-) – kleopatra Jan 12 '12 at 13:56
  • @kleopatra You were right. _"The base problem is the assumption that all components do have the default cursor"._ I could fix it in my SSCCE by excluding JTableHeader while setting the Cursor to WAIT and back to DEFAULT. I need to apply this into my app and check it there. Thanks for the answer. I dont have the privilege to vote up now. So a +1 here. – AnupD Jan 13 '12 at 04:57
  • @mKorbel Thanks for your valid help – AnupD Jan 13 '12 at 04:59
1

1) you have redirect code

for(int i=0; i<2000; i++) {
    System.out.print(i);
}

to the BackGround Task, you can implements javax.swing.Timer, SwingWorker, or wrap these code lines inside Runnable#Thread, example here

2) you have to restore Cursor on Error/Exception too, that's probably reason why Cursor wasn't changed

Community
  • 1
  • 1
mKorbel
  • 109,525
  • 20
  • 134
  • 319
  • I am afraid that you understood my problem correctly. Before I click button, if I place my cursor over the gap between table column headers, the cursor will be <-> means I can resize. When I click on the button, inside action performed, it will change the cursor to hour glass and start the action. when action is finished the cursor is changed to default cursor and leave action performed. After that if I place cursor on table header column gap, the icon is default icon, not <->. This happens if i click the button a few times. But i am still able to resize the column. – AnupD Jan 12 '12 at 11:33
  • not able to generating your issue, nor if I push JButton lots of times, I redirected Cursor to the JFrame, JButton, JTable and I can genirating this issue with Cursor, when and only if is there generated some exception and Cursor is not changed in the finally block, then there Cursor.WAIT_CURSOR, hmm stay forever – mKorbel Jan 12 '12 at 11:48
  • hmm.. I got the exception part, but thats not the case here. I wish if I could share a video or screenshot of the issue with you. – AnupD Jan 12 '12 at 12:44
  • disbeliever :-) easy enough to reproduce, see my addendum – kleopatra Jan 12 '12 at 13:10