Showing posts with label Java. Show all posts
Showing posts with label Java. Show all posts

Friday, October 15, 2010

Preliminary GRIB rendering for OpenMap

I have managed to render GRIB weather data in a OpenMap layer. It should support GRIB data from a numbers of free sources like NOAA.

Looks pretty cool with the isobars, wind arrows and a wind heat map.

The images below are showing a storm front (Xynthia) hitting Western Europe.

















Comments are welcome!

Wednesday, August 18, 2010

Geonames location searcher for OpenMap

Here is an integration of openmap with geonames that will add a combobox that can be used to search geonames.org to the toolbar by adding the following to your openmap.properties file:

openmap.components=... geonamessearcher
geonamessearcher.class=GeoNamesSearcher

Here is the code

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.DefaultComboBoxModel;
import javax.swing.DefaultListCellRenderer;
import javax.swing.JComboBox;
import javax.swing.JComponent;
import javax.swing.JList;
import javax.swing.plaf.basic.BasicComboBoxEditor;

import net.miginfocom.swing.MigLayout;

import org.apache.log4j.Logger;
import org.geonames.Toponym;
import org.geonames.ToponymSearchCriteria;
import org.geonames.ToponymSearchResult;
import org.geonames.WebService;

import com.bbn.openmap.event.CenterListener;
import com.bbn.openmap.event.CenterSupport;
import com.bbn.openmap.gui.OMToolComponent;

public class GeoNamesSearcher extends OMToolComponent {

private static final Logger log = Logger.getLogger(GeoNamesSearcher.class
.getName());

private JComboBox searchBox;

private CenterSupport centerDelegate;

private DefaultComboBoxModel searchBoxModel = new DefaultComboBoxModel();

public GeoNamesSearcher() {
centerDelegate = new CenterSupport(this);
setLayout(new MigLayout());
searchBox = new JComboBox(searchBoxModel);
searchBox.setEditable(true);
searchBox.setMaximumRowCount(25);
searchBox
.setToolTipText("Enter part of location to search for (and center to).");
searchBox.setRenderer(new DefaultListCellRenderer() {
@Override
public JComponent getListCellRendererComponent(JList list,
Object value, int index, boolean isSelected,
boolean cellHasFocus) {
super.getListCellRendererComponent(list, value, index,
isSelected, cellHasFocus);
if (value instanceof Toponym) {
Toponym toponym = (Toponym) value;
setText(toponym.getName() + " " + toponym.getCountryCode());
}
return this;
}
});
searchBox.setEditor(new BasicComboBoxEditor() {
@Override
public void setItem(Object anObject) {
if (anObject != null) {
if (anObject instanceof Toponym) {
Toponym toponym = (Toponym) anObject;
editor.setText(toponym.getName() + " "
+ toponym.getCountryCode());
// oldValue = anObject;
} else {
super.setItem(anObject);
}
} else {
editor.setText("");
}
}
});


searchBox.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent evt) {
JComboBox cb = (JComboBox) evt.getSource();

Object selectedItem = cb.getSelectedItem();

if ("comboBoxEdited".equals(evt.getActionCommand())) {
// User has typed in a string; only possible with an
// editable combobox
try {
searchBoxModel.removeAllElements();
ToponymSearchCriteria searchCriteria = new ToponymSearchCriteria();
// searchCriteria.setCountryCode("DK");
searchCriteria.setQ((String) selectedItem);
ToponymSearchResult searchResult = WebService
.search(searchCriteria);
for (Toponym toponym : searchResult.getToponyms()) {
searchBoxModel.addElement(toponym);
}
if (searchResult.getTotalResultsCount() == 1) {
Toponym t = searchResult.getToponyms().get(0);
centerDelegate.fireCenter(t.getLatitude(), t
.getLongitude());
} else if (searchResult.getTotalResultsCount() > 1) {
searchBox.showPopup();
}
} catch (Exception e) {
log.error("", e);
}

} else if ("comboBoxChanged".equals(evt.getActionCommand())) {
// User has selected an item; it may be the same item
if (cb.getSelectedItem() instanceof Toponym) {
Toponym newItem = (Toponym) cb.getSelectedItem();
centerDelegate.fireCenter(newItem.getLatitude(),
newItem.getLongitude());
}
}

}
});
add(searchBox);
}

public synchronized void addCenterListener(CenterListener listener) {
centerDelegate.add(listener);
}

/**
* Remove a CenterListener
*
* @param listener
* CenterListener
*/
public synchronized void removeCenterListener(CenterListener listener) {
centerDelegate.remove(listener);
}

@Override
public void findAndInit(Object obj) {
if (obj instanceof CenterListener) {
addCenterListener((CenterListener) obj);
}
}

@Override
public void findAndUndo(Object obj) {
if (obj instanceof CenterListener) {
removeCenterListener((CenterListener) obj);
}
}
}

Wednesday, September 23, 2009

Using JavaFX Classes Directly From Java

Sitting on the Swing side of the fence the grass on the JavaFX side certainly sometimes seems a lot greener.


Knowing that the latest and greatest scene graph code is available somewhere in the pile of JavaFX jars it is tempting to try and get access to it from the Swing side without being "bothered" with the JavaFX language.

Other people like Matt Hicks have had the same thoughts (see I Hate JavaFX; I Love JavaFX!) so I started out with trying Matt's port of the JavaFX Clock and after some fiddling I got it up and running with JavaFX 1.2. Try it by pushing the launch button below.



The code follows (add all the JavaFX SDK jars to your classpath before trying this):

/**
* @author Matt Hicks (matt@matthicks.com)
*/
public class Clock {
private float radius = 77;
private float centerX = 144;
private float centerY = 144;
private Calendar calendar = Calendar.getInstance();
private int hours;
private int minutes;
private int seconds;

private FloatVariable hoursVariable;
private FloatVariable minutesVariable;
private FloatVariable secondsVariable;

public Clock() throws IOException {
nextTick();

// Build JavaFX clock
Group group = new Group();
{
ImageView imageView = new ImageView();
Image image = new Image();
// should be image.set$url() but does not work ?
image.loc$platformImage().set(
ImageIO.read(getClass().getClassLoader().getResource(
"clock_background.png")));
imageView.set$image(image);
group.loc$content().insert(imageView);

Group face = new Group();
{
Translate translate = new Translate();
translate.set$x(centerX);
translate.set$y(centerY);
face.loc$transforms.insert(translate);

// Every third hour
for (int i = 3; i <= 12; i += 3) {
Text text = new Text();
translate = new Translate();
translate.set$x(-5.0f);
translate.set$y(5.0f);
text.loc$transforms.insert(translate);
text.set$font(Font.font("Arial", 16));
text.set$x(radius * ((i + 0) % 2 * (2 - i / 3)));
text.set$y(radius * ((i + 1) % 2 * (3 - i / 3)));
text.set$content(String.valueOf(i));
face.loc$content.insert(text);
}

// Black circle for the rest of the hours
for (int i = 1; i < 12; i++) {
if (i % 3 == 0) {
continue; // Don't show a circle on every third hour
}

Circle circle = new Circle();
Rotate rotate = new Rotate();
rotate.set$angle(30.0f * i);
circle.loc$transforms.insert(rotate);
circle.set$centerX(radius);
circle.set$radius(3.0f);
circle.set$fill(Color.$BLACK);
face.loc$content.insert(circle);
}

// Center circles
Circle circle = new Circle();
circle.set$radius(5.0f);
circle.set$fill(Color.$DARKRED);

face.loc$content.insert(circle);
circle = new Circle();
circle.set$radius(3.0f);
circle.set$fill(Color.$RED);
face.loc$content.insert(circle);

// Second hand
Line line = new Line();
{
Rotate rotate = new Rotate();
BindingExpression exp = new AbstractBindingExpression() {
@Override
public void compute() {
pushValue(seconds * 6f);
}
};
secondsVariable = FloatVariable.make(exp);
rotate.loc$angle().bind(false, secondsVariable);

line.loc$transforms.insert(rotate);

line.set$endY(-radius - 3.0f);
line.set$strokeWidth(2.0f);
line.set$stroke(Color.$RED);
}
face.loc$content.insert(line);

// Hour hand
Path path = new Path();
{
Rotate rotate = new Rotate();
BindingExpression exp = new AbstractBindingExpression() {
@Override
public void compute() {
pushValue((float) (hours + minutes / 60) * 30 - 90);
}
};

hoursVariable = FloatVariable.make(exp);
rotate.loc$angle().bind(false, hoursVariable);
path.loc$transforms.insert(rotate);

path.set$fill(Color.$BLACK);

MoveTo e1 = new MoveTo();
e1.set$x(4.0f);
e1.set$y(4.0f);
path.loc$elements.insert(e1);
ArcTo e2 = new ArcTo();
e2.set$x(4.0f);
e2.set$y(-4.0f);
e2.set$radiusX(1.0f);
e2.set$radiusY(1.0f);
path.loc$elements.insert(e2);
LineTo e3 = new LineTo();
e3.set$x(radius - 15.0f);
e3.set$y(0.0f);
path.loc$elements.insert(e3);
}
face.loc$content.insert(path);

// Minute hand
path = new Path();
{
Rotate rotate = new Rotate();
BindingExpression exp = new AbstractBindingExpression() {
public void compute() {
pushValue((float) minutes * 6 - 90);
}
};
minutesVariable = FloatVariable.make(exp);
rotate.loc$angle().bind(false, minutesVariable);

path.loc$transforms.insert(rotate);
path.set$fill(Color.$BLACK);

MoveTo e1 = new MoveTo();
e1.set$x(4.0f);
e1.set$y(4.0f);
path.loc$elements.insert(e1);
ArcTo e2 = new ArcTo();
e2.set$x(4.0f);
e2.set$y(-4.0f);
e2.set$radiusX(1.0f);
e2.set$radiusY(1.0f);
path.loc$elements.insert(e2);
LineTo e3 = new LineTo();
e3.set$x(radius);
e3.set$y(0.0f);
path.loc$elements.insert(e3);
}
face.loc$content.insert(path);

group.loc$content.insert(face);
}
}

Timeline timeline = new Timeline();
timeline.set$repeatCount(Timeline.$INDEFINITE);

KeyFrame kf = new KeyFrame();
kf.set$time(Duration.valueOf(1000.0f));
kf.set$canSkip(true);
kf.set$action(new Function0<Void>() {
public Void invoke() {
nextTick();
return null;
}
});
timeline.loc$keyFrames.insert(kf);
// this is somewhat hairy JFxtras does it like this I think
Scene scene = new Scene();
scene.loc$content().insert(group);
JPanel panel = new JPanel(new BorderLayout());
TKScene fxNode = scene.get$javafx$scene$Scene$impl_peer();
panel.add(((SwingScene) fxNode).scenePanel, BorderLayout.CENTER);
JFrame frame = new JFrame("JavaFX Clock Example");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(panel);
frame.setSize(295, 325);
frame.setVisible(true);
timeline.play();
}

public void nextTick() {
calendar.setTimeInMillis(System.currentTimeMillis());
seconds = calendar.get(Calendar.SECOND);
minutes = calendar.get(Calendar.MINUTE);
hours = calendar.get(Calendar.HOUR_OF_DAY);
// trigger bindings to re calc and move hands
if (secondsVariable != null) {
secondsVariable.invalidate();
minutesVariable.invalidate();
hoursVariable.invalidate();
}
}

public static void main(String[] args) throws Exception {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
try {
new Clock();
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
}

For comparison the JavaFX version follows here:

public class Clock extends CustomNode {

public var radius: Number = 77;
public var centerX: Number = 144;
public var centerY: Number = 144;

public var hours:Number;
public var minutes:Number;
public var seconds:Number;

public function nextTick () {
var now = new Date();
seconds = now.getSeconds();
minutes = now.getMinutes();
hours = now.getHours();
}

public override function create() : Node {
return Group {
content: [
ImageView {
image: Image {
url: "{__DIR__}clock_background.png"
}
},
Group {
transforms: Translate {
x: centerX,
y: centerY
}
content: [
// code to display the numbers for every third hour
for (i in [3, 6, 9, 12])
Text {
transforms: Translate {
x: -5,
y: 5
}
font: Font {
size: 16
}
x: radius * (( i + 0 ) mod 2 * ( 2 - i / 3))
y: radius * (( i + 1 ) mod 2 * ( 3 - i / 3))
content: "{i}"
}, //Text


//code to display a black circle for the rest of the hours on the clock
for (i in [1..12])
if (i mod 3 != 0 ) then Circle {
transforms: Rotate {
angle: 30 * i
}
centerX: radius
radius: 3
fill: Color.BLACK
} //for
else [ ],

// code for the clock's first center circle
Circle {
radius: 5
fill: Color.DARKRED
}, //Circle
//code for the smaller center circle
Circle {
radius: 3
fill: Color.RED
}, //Circle
//code for the seconds hand
Line {
transforms: Rotate {
angle: bind seconds * 6
}
endY: -radius - 3
strokeWidth: 2
stroke: Color.RED
}, //Line
//code for the hour hand
Path {
transforms: Rotate {
angle: bind (hours + minutes / 60) * 30 - 90
}
fill: Color.BLACK
elements: [
MoveTo {
x: 4,
y: 4},
ArcTo {
x: 4
y: -4
radiusX: 1
radiusY: 1},
LineTo{
x: radius - 15
y: 0},
] //elements
}, // Path
// code for the minutes hand
Path {
transforms: Rotate {
angle: bind minutes * 6 - 90
}
fill: Color.BLACK
elements: [
MoveTo {
x: 4,
y: 4},
ArcTo {
x: 4
y: -4
radiusX: 1
radiusY: 1},
LineTo{
x: radius
y: 0},
] // elements
} // Path
] //content
}
]
};
}

init {
var timeline = Timeline {
repeatCount: Timeline.INDEFINITE
keyFrames: [
KeyFrame {
time: 1s
canSkip: true
action: function() {
nextTick();
}
}
]
}
timeline.play();
}
}

Web start depolyment is rather easy as it is possible to take advantage of the JavaFX extension and just add:



to your jnlp file and then do the usual jnlp stuff.

Friday, August 28, 2009

Build your own GPS/GIS system in less than 200 lines with OpenMap


Here is how to do your own (simplistic) GPS/GIS system using OpenMap in less than 200 lines of code. To get started you will need to download and install OpenMap.

The GPS Layer for OpenMap is as follows. Notice that the code is somewhat crude with regards to threading, drizzel some synchronized on to it as appropriate.


public class GPSLayer extends OMGraphicHandlerLayer {

private static String GPSDATA = "gpsData";

private static final double KT2MPS = 1852.0 / 3600.0;

private static final double speedVectorLengthInMinutes = 6;

private String gpsDataPath = "";

private float latitude, longitude, speed, course;

private OMGraphicList graphics = new OMGraphicList();;

private OMRect gpsPosition = new OMRect(0, 0, 0, 0, 10, 10);

private OMLine speedVector;

private OMText gpsText = new OMText(10, 20, "GPS Data",
OMText.JUSTIFY_LEFT);

private Timer timer;

public GPSLayer() {
gpsText.setFillPaint(Color.WHITE);
gpsText.setTextMatteColor(new Color(182, 235, 219));
gpsPosition.setFillPaint(Color.pink);
}

@Override
public void setProperties(String prefix, Properties props) {
super.setProperties(prefix, props);
gpsDataPath = props.getProperty(prefix + "." + GPSDATA);
// redraw every 5 secs
timer = new Timer(5000, this);
timer.start();

// emulate reading GPS data (threading issue here access to members not
// protected!)
new Thread(new Runnable() {
public void run() {
try {
BufferedReader in = new BufferedReader(new FileReader(
gpsDataPath));
String str;
while ((str = in.readLine()) != null) {
Thread.sleep(100);
if (str.startsWith("$GPRMC")) {
String[] fields = str.split(",");
// utc_date = fields[1];
double lat = Double.parseDouble(fields[3]);
double degrees = Math.floor((lat / 100.0));
double minute = (lat / 100.0) - degrees;
lat = (degrees) + ((minute * 100.0) / 60);
if (fields[4].equals("S"))
lat = -lat;
latitude = (float) lat;

double lon = Double.parseDouble(fields[5]);
degrees = Math.floor((lon / 100.0));
minute = (lon / 100.0) - degrees;
lon = (degrees) + ((minute * 100.0) / 60);
if (fields[6].equals("W"))
lon = -lon;
longitude = (float) lon;

speed = (float) (Double.parseDouble(fields[7]) * KT2MPS);

if (!fields[8].equals("")) {
course = (float) Double.parseDouble(fields[8]);
}
// could parse date but to lazy
// date = fields[9];
}
}
in.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}).start();
}

@Override
public synchronized OMGraphicList prepare() {
gpsPosition.setLocation(latitude, longitude, -5, -5, 5, 5);

// calc. speed vector length using OM GIS functions
LatLonPoint startPos = new LatLonPoint(latitude, longitude);
float length = (float) Length.KM.toRadians(speedVectorLengthInMinutes
* ((speed * 3.6) / 60.0));
LatLonPoint endPos = startPos.getPoint(length, (float) ProjMath
.degToRad(course));
speedVector = new OMLine(startPos.getLatitude(), startPos
.getLongitude(), endPos.getLatitude(), endPos.getLongitude(),
OMLine.LINETYPE_STRAIGHT);
// vec.addArrowHead(OMArrowHead.ARROWHEAD_DIRECTION_FORWARD, 100, 3, 1);
speedVector.setLinePaint(Color.DARK_GRAY);

graphics.clear();
// order of add determines what is rendered on top
graphics.add(speedVector);
graphics.add(gpsPosition);
gpsText.setData(String.format("GPS Data\n%4.2f Km/h",
(speed * 3.60)));
graphics.add(gpsText);

graphics.project(getProjection());
return graphics;
}

@Override
public void actionPerformed(ActionEvent ae) {
doPrepare();
}
}

If you have a GPS antenna you can use one of the free GPS servers (gpsd on linux) to make its data available on a socket and then modify the in variable in reader thread to the following:

uc = new URL(gpsDataPath).openConnection(); // gps.gpsData=http://localhost:2244
InputStreamReader icr = new InputStreamReader(uc.getInputStream());
in = new BufferedReader(icr);


To run this put the above class into a jar file and copy it to the lib dir of the OpenMap installation. Then in the share dir create a openmap.properties file with the following content:

openmap.layers=gps graticule shapePolitical
openmap.startUpLayers=gps graticule shapePolitical

gps.class=GPSLayer
gps.prettyName=GPS Position
gps.gpsData=/tmp/gpslog.txt


Then go to the bin dir of the OpenMap installation and do ./openmap(.bat) and your GPS/GIS system will be running.

Then following can be used as test data

$GPRMC,143346,A,5616.9232,N,01008.2504,E,074.6,011.3,110105,000.9,E,A*13
$GPRMC,143347,A,5616.9435,N,01008.2571,E,074.5,010.4,110105,000.9,E,A*14
$GPRMC,143348,A,5616.9638,N,01008.2632,E,074.4,009.5,110105,000.9,E,A*18
$GPRMC,143349,A,5616.9842,N,01008.2686,E,074.3,008.4,110105,000.9,E,A*12

Friday, August 21, 2009

Persisting JXTable's Column Control Settings

Here is how to persist (save/restore) the users settings (ordering of columns, visbility etc) of a JXTable using the Swing Application Framework aka SAF (JSR-296). You will need to download the jar for SAF to get the stuff below working.

(Notice that all of this is inspirede by NetBeans Platform meets Swing Application Framework: SessionStorage but without all the NetBeans clutter.)

/**
*
* @See http://puces-blog.blogspot.com/2009/04/netbeans-platform-meets-swing.html
*/

public class ModuleApplicationContext extends ApplicationContext {

private String storageDirectoryPath = "";

static {

// download from https://jdnc-incubator.dev.java.net/source/browse/jdnc-incubator/trunk/src/kleopatra/java/org/jdesktop/appframework/swingx/XProperties.java?rev=3198&view=markup
new XProperties().registerPersistenceDelegates();
}

public ModuleApplicationContext(String path) {
// Needed due to issue
// https://appframework.dev.java.net/issues/show_bug.cgi?id=112
setLocalStorage(new ModuleLocalStorage(this));
// getLocalStorage().setDirectory(getModuleSessionStorageDir(moduleInfo));
storageDirectoryPath = path;
getLocalStorage().setDirectory(new File(storageDirectoryPath));
getSessionStorage().putProperty(JXTable.class,
new XProperties.XTableProperty());
}

}



/**
* A LocalStorage for modules. It respects the direcory property in JNLP mode.

*

* Needed due to issue * HREF="https://appframework.dev.java.net/issues/show_bug.cgi?id=112">
* https://appframework.dev.java.net/issues/show_bug.cgi?id=112
*
* @author puce
*/

public class ModuleLocalStorage extends LocalStorage {

public ModuleLocalStorage(ApplicationContext context) {
super(context);
}

@Override
public boolean deleteFile(String fileName) throws IOException {
File path = new File(getDirectory(), fileName);
return path.delete();
}

@Override
public InputStream openInputFile(String fileName) throws IOException {
File path = new File(getDirectory(), fileName);
return new BufferedInputStream(new FileInputStream(path));
}

@Override
public OutputStream openOutputFile(String fileName) throws IOException {
File path = new File(getDirectory(), fileName);
return new BufferedOutputStream(new FileOutputStream(path));
}
}


Then you can use the ModuleApplicationContext to save the column control settings of a JXTable doing something like below:


public class TestJXTable extends JFrame {

String data[][] = { { "John", "Sutherland", "Student" },
{ "George", "Davies", "Student" },
{ "Melissa", "Anderson", "Associate" },
{ "Stergios", "Maglaras", "Developer" }, };

String fields[] = { "Name", "Surname", "Status" };

ModuleApplicationContext mac;

JXTable jt;

JScrollPane pane;

public static void main(String[] argv) {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
TestJXTable myExample = new TestJXTable(
"JXTable Example");
}
});
}

public TestJXTable(String title) {
super(title);
// save settings in users home dir
mac = new ModuleApplicationContext(System.getProperty("user.home"));
setSize(150, 150);
addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent we) {
dispose();
try {
jt.getParent().remove(jt);
mac.getSessionStorage().save(jt, "testTable.xml");
} catch (IOException e) {
e.printStackTrace();
}
System.exit(0);
}
});
init();
pack();
setVisible(true);
}

private void init() {
jt = new JXTable(data, fields);
jt.setColumnControlVisible(true);
jt.setName("testTable");
pane = new JScrollPane(jt);
pane.setName("testPane");
try {
mac.getSessionStorage().restore(jt, "testTable.xml");
} catch (IOException e) {
e.printStackTrace();
}
getContentPane().add(pane);
}
}

Tuesday, August 18, 2009

Font sizer in OpenMap statusbar

If you run OpenMap (beta5) under the Substance 5.2 LAF you can add a font size slider to the OpenMap statusbar doing this:


public class FontPanel extends OMComponentPanel {

public FontPanel() {
}

@Override
public void findAndInit(Object obj) {
if (obj instanceof InformationDelegator) {
InformationDelegator delgator = (InformationDelegator) obj;
GridBagConstraints c = new GridBagConstraints();
c.weightx = 1;
c.weighty = 1;
c.anchor = GridBagConstraints.EAST;
c.fill = GridBagConstraints.HORIZONTAL;
c.insets = new Insets(0, 0, 0, 4);
Component statusPanel = DialogUtils.getChildNamed(delgator,
StatusLightPanel.class);
delgator.remove(statusPanel);
delgator.add(FontSizePanel.getPanel(), c);
delgator.add(statusPanel);
}
}
}


Add the above class to your openmap.components in openmap.properties and then run OM with
-Dswing.defaultlaf=org.jvnet.substance.skin.SubstanceCremeLookAndFeel
Click here to see how this looks.

FontSizePanel.java can be found here or downloaded from the Substance homepage.

Substance with VLDocking Framework


Here is a simple UI integration of the VLDocking Framework with Substance 5.2.

In main do:

DockingUISettings.getInstance().installUI();
//and start customizing... MyDockViewTitleBarUI
UIManager.put("DockViewTitleBarUI", "MyDockViewTitleBarUI");


public class MyDockViewTitleBarUI extends DockViewTitleBarUI {

public MyDockViewTitleBarUI(DockViewTitleBar tb) {
super(tb);
SubstanceLookAndFeel.setDecorationType(tb,
DecorationAreaType.PRIMARY_TITLE_PANE);
tb.setForeground(SubstanceColorUtilities
.getForegroundColor(SubstanceColorSchemeUtilities
.getColorScheme(tb, ComponentState.ACTIVE)));
}



static public MyDockViewTitleBarUI createUI(JComponent tb) {
return new MyDockViewTitleBarUI((DockViewTitleBar) tb);
}

@Override
public void paint(Graphics g, JComponent c) {
DockViewTitleBar tb = (DockViewTitleBar) c;

SubstanceSkin skin = SubstanceCoreUtilities.getSkin(tb);
if (skin != null) {
SubstanceDecorationUtilities
.paintDecorationBackground(g, tb, false);
} else {
super.paint(g, tb);
}
}
}

JXTable Striping with Substance 5.2

Here is how I got table striping to work on JXTable with Substance 5.2.


public class DefaultTableFactory implements TableFactory {

public JTable createTable() {
JXTable result = new JXTable();
return configureTable(result);
}

public JTable createTable(TableModel model) {
JXTable result = new JXTable(model);
return configureTable(result);
}

private JXTable configureTable(JXTable result) {
result.getSelectionMapper().setEnabled(false);
result.setColumnControlVisible(true);
result.setHighlighters(createHighlighter(result), new ColorHighlighter(
HighlightPredicate.ROLLOVER_ROW, null, Color.BLUE));
result.setRolloverEnabled(true);
result.setHorizontalScrollEnabled(true);
return result;
}

public CompoundHighlighter createHighlighter(JXTable t) {
ColorHighlighter first = new SubstanceHighLighter(HighlightPredicate.EVEN, t);
ColorHighlighter hl = new SubstanceHighLighter(HighlightPredicate.ODD,t);
return new CompoundHighlighter(first, hl);
}

// get striping on jxtable to work with substance
public class SubstanceHighLighter extends ColorHighlighter {

private JXTable comp;

SubstanceHighLighter(HighlightPredicate pred, JXTable t) {
setHighlightPredicate(pred);
comp = t;
}

@Override
public Color getBackground() {
return SubstanceColorUtilities.getStripedBackground(comp,
getHighlightPredicate() == HighlightPredicate.EVEN ? 1 : 0);
}

}

There is still a problem with some of the cell renders installed by JXTable...

Live Traffic Map