Swing controls work under Java 6, not under Java 7 - java

I'm performing maintenance on an existing application. The feature I'm working on used to work when compiled under Java 6, but doesn't under Java 7. I'll try to be brief.
The app has a huge JTabbedPane that composes almost the entire UI. The JTabbedPane contains a number of JPanels. Each JPanel, in turn, contains an obscene number of JComponents. The user can enter values into the controls, but if--for some reason--they want to clear out all their changes, they can select an option to "reset" the panel. The way the responsible method works is it removes the JPanel, recreates a new one of the exact same class (they all inherit from JPanel) and re-inserts it at the same index into the JTabbedPane. Simple, right?
But what actually happens is it switches to the next JPanel tab in the JTabbedPane and then a number of controls from the new panel show up overlapping the controls in the next panel. So, for example, if I had panel0 selected and wanted to reset it, it would show panel1 as selected and a whole bunch of panel0's components overlapping it.
It gets even weirder. No other tabs can be selected after this happens. It's like the UI freezes. The user has to select the current tab (i.e. panel1) by clicking its tab on top (which is strange because it's already selected). Then they can go to other tabs and everything acts as it should. But if they don't know the workaround, it's rather frightening. And it's just plain wrong. Reseting the panel should just show the same panel with all of the controls blank.
Here is what is done, in pseudo-code:
Get the current panel
Create a new instance of it
Remove current panel
Insert new instance
Revalidate & repaint
Set the selected index of the panel
Here is the actual code:
public void resetCurrentTab( int currentTabIndex ) {
// tried using m_tabbedPane.getTabComponentAt( currentTabIndex), but that returns null
JPanel curTab = (JPanel)m_tabbedPane.getSelectedComponent();
if( curTab == null )
return;
String curTabName = curTab.getName().trim();
// create new instance of panel
JPanel newPanel = null;
try {
newPanel = curTab.getClass().newInstance();
} catch (InstantiationException e) {
log.equals("Could not reset tab... tab " + curTabName + " creation failed.");
return;
} catch (IllegalAccessException e) {
log.equals("Could not reset tab... tab " + curTabName + " creation failed.");
return;
}
if( newPanel != null ) {
m_tabbedPane.remove( currentTabIndex );
m_tabbedPane.insertTab( newPanel.getName(), null, newPanel, null, currentTabIndex );
newPanel.setVisible( true );
}
m_tabbedPane.revalidate();
m_tabbedPane.repaint();
m_tabbedPane.setSelectedIndex( currentTabIndex );
}
Like I said, this works compiled under Java 6. Under Java 7, weirdness happens. I tried all the obvious things, like changing the order of the calls to revalidate(), repaint() and setSelectedIndex(), but that doesn't change the behavior. I also tried just taking out and re-adding the same JPanel just to see what would happen. I don't get the overlapping controls weirdness, but it does go and select the next JPanel (tab) in the tabbed control.
tl;dr
A feature that runs fine compiled under Java 6 doesn't work compiled under Java 7.
Any ideas?
EDIT: I traced the problem back to something else entirely. Before the user resets the tab, a method pops up a JOptionPane asking if they're sure they want to do it. It's just using the simple YES/NO version of .showConfirmDialog(). If I remove the dialog, the feature works perfectly. If I use the dialog, I get the screwy behavior.
int response = JOptionPane
.showConfirmDialog(
currentTab,
confirmMsg,
"Reload the '" + curTabName + "' Tab",
JOptionPane.YES_NO_OPTION,
JOptionPane.QUESTION_MESSAGE );
if( response == JOptionPane.YES_OPTION )
// return a value indicating to reset the panel
I tried a few different values for the first parameter, but none alter the behavior. As you can see, the dialog falls out of scope long before the resetting occurs. Still don't know why this worked under Java 6 and not 7.

Thanks for all the suggestions.
I fixed this in an unusual way, but it's an unusual bug. I removed the Panel (called curTab in the code above) in the method was previously done, then set a flag. The TabbedPane (I had to create my own, inherited from JTabbedPane) looks for the flag in a custom Paint() method. I don't do any custom painting, but in the method, I allocate the new Panel and insert it into the TabbedPane at the index the old one was removed from. Then I clear all flags and clean up everything.
Like I said, it's an unusual fix, but it's the only way I tried I could avoid the visual and functionality glitch.

Related

How do I re-initialize a panel with a GridLayout?

I'm writing a small Calendar application which lets my manage important dates and events of my study. I'm working with java.awt.
A single month is drawn onto a panel with a 7*7 BorderLayout. When the program is started, the panel is drawn with the following method:
public void drawCalendarP(String dateS){
if(mf != null && calendarP != null){
mf.remove(calendarP);
calendarP.removeAll();
}
calendarP = new Panel();
calendarP.setLayout(new GridLayout(7,7,10,10));
//...
day[0] = new Button("1");
day[0].addActionListener(...);
day[0].setVisible(true);
calendarP.add(day[0]);
/*each day of the month is represented by a button. I've put the
initialization of the 'Day-buttons' in its own method which is called
frome here, but this is basically what it does.*/
//...
mainframe.add(calendarP);
}
}
Now, when I start the program and this method is firstly called, everything works just as intended. However, I included a button which lets you switch between individual months, and this is where the problem arises.
Because in order to draw a new month, I have decided to just call the method drawCalenderP() again, but with a different String dateS. And again, if you put this String in manually before the start of the program, it will happily draw you any month you want.
However, if I call the method again with said button, it will do the following:
It will re-draw the panel, along with its background and everything (as intended)
It will re-initialize all the components for that panel (as intended)
But it wont draw the new components back onto the panel (not as intended)
I have no idea why that happens. It should add the new components, just the way it does when you call the method the first time, but the components just won't show up on the panel.
The thing is; if I draw the button without the help of the GridLayout, let's say with setSize(w,x) and setLocation(y,z), everything will work just fine again. So the problem lies somewhere within the GridLayout, but I just can't figure it out.
Is there a way to do this without scrapping the entire layout-thing and doing the positioning of the components manually?

Switch between multiple JFrames

I'm relatively new to Java and I'm trying to make some kind of quiz. I created 3 JFrames, all in the same package. On my main frame, there are two buttons (one for the english version and the other one for the german version). I want to switch JFrames after pressing these buttons (so that I can, by pressing "English", see and interact with my english quiz frame). Looking it up didn't help me the slightest, because I'm not really experienced with it. Is it even possible to do it like this? If not, how could I do it?
The standard approach is to use the Card Layout, which allows you to use the same JFrame as you populate it with different things at different points in your application. So initially, your JFrame would show the loading screen, then the user presses a button and you load a new set of components without discarding the current JFrame you have. In some cases, you might also need to make some size adjustments.
It is difficult to say without seeing any code, but usually, what is done is that you do something like so:
new Frame(args);
this.dispose();
The code above assumes that the constructor of Frame takes care of launching and making the components visible. The this.dispose(); disposes of the current JFrame (assuming your class extends JFrame).
You have two buttons in your frame 1 right? So first, double click the button which says "English". Lets say the variable name for that button is jButton1. Inside that button type this.
private void jButton1ActionPerformed(java.awt.event.ActionEvent evt) {
this.dispose();
EnglishFrame eng = new EnglishFrame();
eng.setVisible(true);
}
Then double click the other button which says "German" (jButton2). Inside that type this.
private void jButton2ActionPerformed(java.awt.event.ActionEvent evt) {
this.dispose();
GermanFrame german = new GermanFrame();
german.setVisible(true);
}
this.dispose() - This will cause the jFrame window to close
Then you create an object of the other two forms. (In your case the form for English and Germany)
.setVisible(true) - This will show you the form.
Create a single Jframe window. After that create JPanels with all the compenents such as buttons, textfields and labels you want. Make sure the panel is the same size as your Jframe. Panel's work about the same as JFrame's, code wise.
This code will stitch everything together for you:
panel.setSize(Jframe.getSize()) //That wont
panel.add(button); //Just remember you need to add more code to position the buttons correctly.
//If you using netbeans builder:
//You just have to use this one line in the constructor/intialiser method
frame.add(panel); //This will add the panel to the Jframe/Window
//No need to add extra code for positioning.
If you want to swap between the panels. In the button press method, use this
frame.setContentPane(panel); //panel = panel you want to change too.
frame.repaint(); //Ensures that the frame swaps to the next panel and doesn't get stuck.
frame.revalidate(); //Ensures that the frame swaps to the next panel and doesn't get stuck.
When you first start the java application you have to set the content pane or else it will appear as a blank window.
frame.setContentPane(panel); //Starting Panel
frame.setVisible(true); //Make the frame visible
Sorry if the explanation is bad, I don't have enough time to explain it fully.

JXTreeTable scroll upon adding new node?

I have a JXTreeTable with an add button. The button works as expected: it adds a new node to the end, with values that (for the purpose of testing) are hard-coded into my program. The line:
modelSupport.fireChildAdded(new TreePath(root), noOfChildren-1, dataNode);
tells the JXTreeTable to recognise the update made to the TreeTableModel, and to refresh accordingly.
I then execute:
treeTable.changeSelection(treeTable.getRowCount()-1, 0, false, false);
to automatically select this new node. This works as expected, BUT I also expected it to automatically scroll to bring this new node into sight, which it does not. Instead, it scrolls to show only the penultimate node, leaving the new node hidden just slightly beyond view.
I've also tried using tricks to programmatically scroll to the final record. However, this code:
JScrollBar vertical = scrollPane.getVerticalScrollBar();
vertical.setValue(vertical.getMaximum());
revealed the same problem.
Bizarrely, if I attach either of the above two blocks of code to a "Scroll to Bottom" button, and press this button manually after adding the new node, it all works fine! Please can anyone isolate the cause of this bug, or offer a fix/workaround?
JXTreeTable should have something like:
JXTreeTable.scrollPathToVisible(...)
Does this work for you?
EDIT:
I assume it works with your Button, because the Scrollpane is already "rendered/painted" with the new added node (when you click the button).based on this, the internal max/min calculation (e.g in scrollpane) can differ.
For anyone else having this problem, it seems to be caused by a peculiarity with the order in which GUI actions are executed (due in part to the need to be executed on the Dispatch thread).
From memory, this is how I solved it. (I haven't looked at the code in several months, so this code might not work perfectly, but you get the idea.)
Wrap the "vertical.setValue(vertical.getMaximum());" code in a SwingUtilities.invokeLater() method call. In other words:
SwingUtilities.invokeLater(new Runnable() {
public void run() {
JScrollBar vertical = scrollPane.getVerticalScrollBar();
vertical.setValue(vertical.getMaximum());
}
});

Adding to JPanel in application

When a particular button is clicked I want another set of buttons to be added to the Panel, however at the moment when I do this, I can add them as many times as I want, I need this to be only possible once. Would the best way to do this be set the adding of the buttons and fields in a while loop?
if(e.getSource() == selectScript){
while(scriptB < 1 ){
imageID = new JTextField("INT");
imageDescription = new JTextField("imgDescription");
imagePath = new JTextField("imagePath");
manageImageTab.add(imageID);
manageImageTab.add(imageDescription);
manageImageTab.add(imagePath);
insertImage = new JButton("Add an Image");
insertImage.addActionListener(new dbaccess());
manageImageTab.add(insertImage);
manageImageTab.revalidate();
validate();
scriptB++;
}
}
Perhaps rather than add and remove the JButtons, you could add the buttons once at the code start, just don't make them visible until you need them, or perhaps better place them all on a JPanel that is not visible and then made visible when desired. Just don't forget to call revalidate() and repaint() on the container that holds the buttons and their panel.
If I understand you correctly, I would use a flag alreadyAdded that starts out false, gets set to true after the controls have been added, then don't allow it to add after that.

Miglayout + hidemode 3 = unwanted autoscroll when showing controls

I'm using 'hidemode 3' in MigLayout so that hidden components aren't visible at all (and don't have any impact on layout).
I'm using this so that I can show inline errors underneath textboxes, which show only if there is an error
Whenever I show these inline error boxes (they're text areas so I just call setVisible(true)) the scroll pane the form is embedded within automatically scrolls down to whatever is being set to visible (so I call setVisible(true) on something, it causes a layout change and the scroll pane auto-scrolls downward to where ever the component is)
Now, this isn't the problem - I think I get why the above part happens (presuambly the panel changing size to accomodate the new layout screws up the scale of the scroll bar, so it appears to scroll downward)
What I can't understand is how to work around it - for example I've tried doing this:
// validateModel will cause the setVisible() calls to occur
if (!syncControl_.validateModel())
{
// Here I try and counteract the layout change by going back
// to the top
variableScrollPane_.getViewport().setViewPosition(new Point(0,0));
}
But it doesn't work - or rather, it does work for a moment: The scroll occurs, but the apparent 'auto scroll' then happens a few milliseconds afterwards. It seems whatever redoes the layout either gets called at regular intervals, or setVisible actually fires some kind of event
I've tried calling validate(), invalidate() repaint() etc. prior to the scroll change to no avail.
So I suppose my question is: When I use hidemode 3 and call setVisible() what method is it that does the 'reacting'? Is it during validation / doLayout() etc. or is there something completely separate happening?
Thanks
Just accidentally answered my own question:
SwingUtilities.invokeLater(new Runnable() {
public void run() {
variableScrollPane_.getVerticalScrollBar().setValue(0);
}
});
Turns out MigLayout doesn't factor into this at all (and it seems the above is a well-known mechanism for resetting the scrollbar in many situations)

Categories

Resources