Hello Beni,
Long time no chat. Here’s a bug I found totally by accident.
The other day I turned on debugging in my application to do some testing. I went out to lunch and when I got back, I had a massively large log file. Nobody was using my PC (the screen was dark). When I reviewed the log file, there were thousands and thousands of the getValueAt() method of a TableModel being called.
I have spent the last 2 days trying to figure out what is invoking the getValueAt() method of a TableModel over and over again.
- I checked the older version of the application before Docking Frames and there were no issues.
- In the current release, I commented out all Docking Frame components in the application and added the JScrollPane with JTable directly to the JFrame and there were no issues.
- So, I uncommented everything and started piece by piece commenting out different parts of the code until I finally tracked down the issue (what a pain!!).
Many years ago, I asked you „How do I add a background image to cwArea?“ see: Background image for WorkingArea and you gave me the example BackgroundIconTest class. I have been using it without issue or at least I didn’t realize that there was an issue.
On startup, the paint method of the LogoBackgroundPaint gets invoked many times a second which is not great but ok. When you open docks in the CWorkingArea that is where things go bad. Even though the image is covered by the new dock with JTable, the paint method of the LogoBackgroundPaint is still getting invoked many times a second. This causes the ALL of the components over the background image (which is hidden) to be repainted/revalidated.
Hence, that is why the getValueAt() of a TableModel being called.
I figured since this is really weird that you would want to have an example that shows the issue. The image I use for the background is an animated GIF. I put the URL to an animated GIF in the code for you to download it. Comment out the call to installBackground() and see how its suppose to run. Then run it with the Background image and you will see it go wild.
Here is the code:
import java.awt.*;
import java.awt.event.*;
import java.text.SimpleDateFormat;
import java.util.*;
import javax.swing.*;
import javax.swing.table.DefaultTableModel;
import bibliothek.extension.gui.dock.theme.flat.FlatStationPaint;
import bibliothek.gui.dock.common.*;
import bibliothek.gui.dock.common.intern.*;
import bibliothek.gui.dock.common.theme.*;
import bibliothek.gui.dock.station.*;
import bibliothek.gui.dock.themes.*;
import bibliothek.gui.dock.toolbar.*;
import bibliothek.gui.dock.util.*;
public class Test_DF3 extends JFrame implements ActionListener
{
private static final SimpleDateFormat LOGGER_TIMESTAMP = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss.SSS");
private CControl control = null;
private CWorkingArea cwArea = null;
private int count = 1;
private Image image = null;
public Test_DF3()
{
super();
logger("");
/*
* Get the GIF from: https://i.pinimg.com/originals/e9/fa/42/e9fa42a3ec0a38efc9d1ef2db3f12f3d.gif
* I put a copy of the GIF here: https://www.capitalware.com/dl/products/mqve2/e9fa42a3ec0a38efc9d1ef2db3f12f3d.gif
*/
ImageIcon ii = new ImageIcon(Test_DF3.class.getResource("e9fa42a3ec0a38efc9d1ef2db3f12f3d.gif"));
image = ii.getImage();
createMenubar();
control = new CControl(this);
control.setTheme(ThemeMap.KEY_ECLIPSE_THEME);
((CEclipseTheme) control.getController().getTheme()).intern().setPaint(new FlatStationPaint());
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setSize(1024, 750);
CToolbarContentArea ctcArea = new CToolbarContentArea( control, "base" );
control.addStationContainer( ctcArea );
/* ... and added to the frame */
this.add( ctcArea );
cwArea = control.createWorkingArea( "work" );
installBackground( control, cwArea );
CGrid grid = new CGrid(control);
grid.add( 0, 0, 100, 100, cwArea ); // x,y w,h
ctcArea.getCenterArea().deploy(grid);
this.setVisible(true);
}
private void createMenubar()
{
logger("");
JMenuBar menuBar = new JMenuBar();
this.setJMenuBar(menuBar);
JMenu fileMenu = new JMenu("File");
fileMenu.setOpaque(true);
menuBar.add(fileMenu);
JMenuItem newEditor = new JMenuItem( "New Editor" );
newEditor.setActionCommand("NewEditor");
newEditor.addActionListener(this);
fileMenu.add( newEditor );
JMenuItem closeAllEditors = new JMenuItem( "Close All Editors" );
closeAllEditors.setActionCommand("CloseAllEditors");
closeAllEditors.addActionListener(this);
fileMenu.add( closeAllEditors );
}
@Override
public void actionPerformed(ActionEvent e)
{
String actionCmd = e.getActionCommand();
logger("actionCmd="+actionCmd);
if ("NewEditor".equals(actionCmd))
{
DefaultMultipleCDockable editor = new DefaultMultipleCDockable( null );
editor.setTitleText( "Editor " + (count++) );
editor.setCloseable( true );
editor.setExternalizable(false);
editor.setRemoveOnClose(true);
Object[][] data =
{
{"xxxx", "5"},
{"yyyyy", "Abc"},
{"zz", "7898"},
{"aaaa", "157"},
{"BBBB", "9876"},
};
String[] headers = {"Field", "Value"};
DefaultTableModel dtm = new DefaultTableModel(5, 2)
{
@Override
public Object getValueAt(int r, int c)
{
logger("r="+r+" : c="+c);
return ((Vector) dataVector.get(r)).get(c);
}
};
dtm.setDataVector(data, headers);
JTable jt = new JTable(dtm);
jt.setRowSelectionAllowed(true);
editor.add( new JScrollPane(jt,
JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED,
JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED));
cwArea.show( editor );
editor.toFront();
}
else if ("CloseAllEditors".equals(actionCmd))
{
int c = control.getCDockableCount();
for (int i=(c-1); i >= 0; i--)
{
logger("i="+i);
CDockable focus = control.getCDockable(i);
if( (focus != null) && (focus.getWorkingArea() == cwArea) )
{
control.removeDockable((MultipleCDockable)focus);
}
}
}
}
private void installBackground( CControl control, final CWorkingArea area )
{
ThemeManager themeManager = control.getController().getThemeManager();
// custom UIBridge may install custom background to any DockStation (note: there can only be one bridge for each KIND of component)
themeManager.setBackgroundPaintBridge( StationBackgroundComponent.KIND, new UIBridge<BackgroundPaint, UIValue<BackgroundPaint>>()
{
public void set( String id, BackgroundPaint value, UIValue<BackgroundPaint> uiValue )
{
logger("");
// this cast is safe, because we installed this bridge with the constant StationBackgroundComponent.KIND
StationBackgroundComponent component = (StationBackgroundComponent)uiValue;
if( area.getStation() == component.getStation())
{
logger("[if] id="+id+ " : value=" + value + " : uiValue="+uiValue);
uiValue.set( new LogoBackgroundPaint() );
}
else
{
logger("[else] id="+id+ " : value=" + value + " : uiValue="+uiValue);
uiValue.set( value );
}
}
public void remove( String id, UIValue<BackgroundPaint> uiValue )
{
logger("");
}
public void add( String id, UIValue<BackgroundPaint> uiValue )
{
logger(" id="+id+ " : uiValue="+uiValue);
}
});
}
private class LogoBackgroundPaint implements BackgroundPaint
{
public void install( BackgroundComponent component )
{
logger("");
}
public void uninstall( BackgroundComponent component )
{
logger("");
}
public void paint( BackgroundComponent background, PaintableComponent paintable, Graphics g )
{
logger("");
// don't forget to paint the ordinary background first
paintable.paintBackground( g );
// then paint a custom logo
Component c = paintable.getComponent();
// The icon is 267 width, so subtract it then divide by 2 to figure out where to start drawing
int xp = (c.getWidth()-267)/2;
if (image != null)
g.drawImage(image, xp, 28, c); // 28 is to push the logo down a little bit otherwise you can see it after a tab is opened.
}
}
/**
* A simple logging method
* @param data
*/
public static void logger(String data)
{
String className = Thread.currentThread().getStackTrace()[2].getClassName();
// Remove the package info.
if ( (className != null) && (className.lastIndexOf('.') != -1) )
className = className.substring(className.lastIndexOf('.')+1);
System.out.println(LOGGER_TIMESTAMP.format(new Date())+" "+className+": "+Thread.currentThread().getStackTrace()[2].getMethodName()+": "+data);
}
public static void main(String[] args)
{
logger("");
try
{
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
}
catch(Exception e)
{
e.printStackTrace();
}
new Test_DF3();
}
}
Regards,
Roger