Creating Java Swing components – the List Index Bar

In what may turn out to be a regular feature, I wanted to start talking about some of the work I do involving the creation of custom Java Swing components. As anyone who has seen the GUIs I create will know, I quite often create custom components for a variety of purposes. It’s not difficult to write custom Swing components, but there are a few hints and tips which can make this easier and result in well performing components. Hopefully this series will be interesting to those readers who are using Java Swing and inspire them to create their own interesting custom components.

So… let’s get started!

For one of the Java Swing GUI projects I’m working on there’s a requirement to display a visual representation of the index of a list of items with particular items in this index highlighted according to certain criteria. Think along the lines of the margin shown in intelligent code editors used to highlight warnings and errors in the code (such as the right hand margin in IntelliJ IDEA), and you’re not too far from the mark.

This is quite a specialised component so obviously doesn’t exist in the standard Swing set, but it’s a nice little component to write so I thought I’d share with you how I created it. To show you exactly what this component will look like when finished there’s a screenshot of it in action on the right.

Getting started

This will be a fairly simple component in terms of graphical content and behaviour, and there are no existing Swing components which are similar to it, so we will derive our new component from the base Swing component class, JComponent.

public class ListIndexBar extends JComponent {

}

Breaking the design down into the key aspects, we need to consider:

  • keeping track of how many items there are in the list this component represents
  • keeping track of the indices of all items which need to be highlighted
  • calculating the vertical pixel position and height of each item marker based on the current height and the total number of items in the associated list
  • painting the item markers
  • handling mouse events to change the cursor when hovering over item markers and to handle mouse clicks on item markers

So let’s start with the first couple of items.

private int itemCount;

// set of list indices associated with items to be marked
private Set<Integer> markerSet = new HashSet<Integer>();

public int getItemCount() {
  return itemCount;
}
public void setItemCount(int itemCount) {
  this.itemCount = itemCount;
  recalc();  // more on this later
  repaint();  // we've made changes which affect appearance so trigger a repaint
}

public Set<Integer> getMarkerSet() {
  return markerSet;
}

public void addMarkers(Collection<Integer> markers) {
  markerSet.addAll(markers);
  repaint();
}

public void removeMarkers(Collection<Integer> markers) {
  markerSet.removeAll(markers);
  repaint();
}

public void clearMarkers() {
  markerSet.clear();
  repaint();
}

Calculating item marker sizes

Now let’s do the simple calculations associated with working out where each item marker needs to be painted.

We know the height of the component and the total number of items in the associated list so we can calculate the vertical size each item marker needs to be. From this we can work out the vertical pixel position that each item marker needs to be painted at. Note, this calculation needs to be done whenever there is a change in the component size or when the total number of associated list items changes. Accordingly, we call recalc() in the setItemCount method and we also override the two setBounds methods to call recalc() after calling their superclass method.

private double scaleFactor;
private int markerHeight;

private void recalc() {
  scaleFactor = getHeight() / (double) itemCount;
  markerHeight = Math.max(2, (int) scaleFactor);  // markers have min height of 2
}

@Override
public void setBounds(Dimension dimension) {
  super.setBounds(dimension);
  recalc();
}

@Override
public void setBounds(int x, int y, int width, int height) {
  super.setBounds(x, y, width, height);
  recalc();
}

Painting

So now we get to the guts of the component – the painting code. Hopefully this should be self explanatory – there’s nothing too complex going on here.

@Override
protected void paintComponent(Graphics g) {
  // cast to a Graphics2D so we can do more with it
  Graphics2D g2 = (Graphics2D) g;

  // paint or clear the background depending on whether this component is opaque
  // or not. store the current composite so we can restore it later
  Composite composite = g2.getComposite();
  g2.setColor(getBackground());
  if (!isOpaque()) {
    // if not opaque, set the alpha composite to clear the background
    g2.setComposite(AlphaComposite.getInstance(AlphaComposite.DST));
  }
  g2.fillRect(0, 0, getWidth(), getHeight());
  g2.setComposite(composite);  // restore the previous composite

  // markers will be drawn with the foreground colour
  g2.setColor(getForeground());

  int pos;
  for (Integer marker : markerSet) {
    // for each marker, calculate the appropriate Y position
    // and paint a marker of required size
    pos = (int) (marker * scaleFactor);
    g2.fillRect(0, pos, getWidth(), markerHeight);
  }
}

Handling mouse events and finishing off

We’re nearly finished now – the only thing left is to add the code to handle mouse events and initialise the object in its constructor.

We need to handle three types of mouse event:

  • motion events so that we can change the cursor when hovering over an item marker to give a visual cue to the user that the item can be clicked on
  • mouse exit events so we can clear the highlighted index and reset the cursor when the pointer leaves the component
  • mouse click events when hovering over item markers

We also need to keep track of the index of the last highlighted index so it can be included in the selection notification event. If there is no currently highlighted index we set this to -1. We are re-using the existing Swing ListSelectionListener and associated ListSelectionEvent classes here for our notifications for convenience as they are a close match to what we need. We could quite easily enough have used our own custom listener and event classes which are more tailored to this particular situation – but it doesn’t make much difference.

And finally, we do the last bit of initialisation in the constructor, including setting the initial number of items and setting an initial minimum and preferred size so that the component appears with a sensible size when first created. Oh, and we provide an alternative no-arg constructor which uses a default item count of 1!

// the index of the currently highlighted marker index
// gets set when the pointer hovers over a marker and cleared when the mouse is moved off a marker
// or the pointer leaves the component completely
private int highlightedIndex = -1;

// keep track of listeners interested in marker selection events
private List<ListSelectionListener> listeners = new ArrayList<ListSelectionListener>();

public ListIndexBar(int itemCount) {
  this.itemCount = itemCount;
  recalc();

  // add a mouse motion listener to track the current highlighted marker
  addMouseMotionListener(new MouseMotionAdapter() {
    @Override
    public void mouseMoved(MouseEvent e) {
      // calculate the list index which is under the mouse pointer
      int pos = (int) (ListIndexBar.this.itemCount * (e.getPoint().getY() / getHeight()));
      if (markerSet.contains(pos)) {
        // we're over one of the markers so record the index and change the cursor
        highlightedIndex = pos;
        setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
      } else {
        // we're not over any marker so clear the highlighted index
        // and reset the cursor
        highlightedIndex = -1;
        setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
      }
    }
  });

  // add a mouse listener to handle mouse clicks on markers
  addMouseListener(new MouseAdapter() {
    @Override
    public void mousePressed(MouseEvent e) {
      if (highlightedIndex != -1) {
        ListSelectionEvent event = new ListSelectionEvent(ListIndexBar.this, highlightedIndex, highlightedIndex, false);
        for (ListSelectionListener listener : listeners) {
          listener.valueChanged(event);
        }
      }
    }

    @Override
    public void mouseExited(MouseEvent e) {
      // clear the highlighted index when we leave this component
      highlightedIndex = -1;
      setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
    }
  });

  // give the component a min and preferred size
  setMinimumSize(new Dimension(16, 60));
  setPreferredSize(new Dimension(16, 60));
}

public ListIndexBar() {
  this(1);
}

public void addSelectionListener(ListSelectionListener listener) {
  listeners.add(listener);
}

public void removeSelectionListener(ListSelectionListener listener) {
  listeners.remove(listener);
}

And that’s it!

Wrapping up

This is not a particularly complex custom component so hopefully the code has been easily understandable and I hope it’s given you an insight into how you can create effective custom components without having to write too much code.

I’ve attached a complete copy of the component code together with an example class making use of the component below.

ListIndexBar component code and example

PlayStation Network goes back online

After what seems like an age, the Sony PlayStation Network is going back online following the forced emergency security overhall necessitated by the major hack attack back in April.

Luckily I’ve been so busy with work and other stuff that I’ve not had much time for the PS3 over these last few weeks, so it hasn’t been that much of a annoyance to me. Having said that, it’s good to have it back and I’m looking forward to some GT5 and Killzone 3 action over the next few days 🙂

New Android dev tools – visual UI designer

I watched a video of one of the Google I/O 2011 talks last night by Xavier Ducrohet and Tor Norbye about the new Android development tools and was particularly interested in the new visual UI designer in ADT 11. I was very impressed with what I saw but one downside is that I believe it is only currently provided as an Eclipse plugin whereas I am 100% an IntelliJ IDEA man. Hopefully an equivalent IDEA plugin will appear before too long.

Using banana plugs with the Onkyo TX-SR876 A/V recevier

 

I use an Onkyo TX-SR876 A/V receiver as the hub of my A/V set up, and a most excellent piece of kit it is too. I bought this receiver from Richer Sounds about 18 months ago for half the recommended retail price to replace my ageing Sony receiver. My old Sony was very low spec when it came to video processing but the Onkyo is pretty full featured allowing upscaling and upconversion of any source, analogue or digital, up to 1080p. This forms the hub of my system with video and audio from all sources (Sky+ HD, PS3, Wii, Pioneer DVD) being upscaled to 1080p and output over HDMI to my Panasonic TH-50PZ700 50″ 1080p plasma.

Anyway, to cut to the main point of this post. This receiver has typical speaker cable binding posts allowing you to either bind in the bare speaker wires or to use banana plugs. However, the posts have black plastic plugs down the centre where the banana plug would be inserted, stopping you using them.

I’d not seen this before but after a bit of research I understood that it was down to some European directive prohibiting their use due to safety concerns. (My take on this is that if someone is stupid enough to stick something in there that they’re not supposed to, let them deal with the consequences, rather than making everyone else suffer!) When I first came to install the receiver I tried to see if these little plastic plugs were removable, but they didn’t appear to be, and rather than use excessive force to get them out when they may not have supposed to come out, I decided to simply wire up the speakers by binding in the bare cable ends.

So, I’ve been running the receiver like this for quite a while now but after reading something on the web the other day about the fact that these plugs can be removed after all I thought I’d just take another look to see if I could get them out…. and I have! By using a small bladed screwdriver inside the hollow plug pushing outwards, I’ve managed to get the plugs out so I can now revert to my preferred means of connection the speaker cables using banana plugs.

I’ve got an Apple iMac!

After years of being a Microsoft Windows user I’ve finally got my first Apple Mac!

A work colleague was selling his 2009 iMac 24″ with 2.6GHz Core Duo CPU, 4GB RAM and a 640GB hard drive at a price that was too good to ignore.

I didn’t really need another computer but for the price I thought it would be worth getting to try out the Apple computer experience. The intention was to see if it would be suitable as a general family computer, to replace the dual core Athlon 64 Windows Vista Pro based Shuttle PC with 20″ NEC monitor we currently use.

I’ve played with friends’ Macs over the years but never really used them on a day to day basis (apart from the early Macs I used to develop on in Smalltalk many years ago!)

First impressions so far are pretty good and it seems like it will be a good fit for a family PC.

Enabling HTML tag formatting in Drupal WYSIWYG / TinyMCE

I use Drupal for many of my more complex web sites – it’s an excellent Content Management System. I also regularly use the TinyMCE WYSIWYG editor to provide rich text editing capability when editing content.

Recently I noticed that I was no longer able to format sections of text using HTML levels such as paragraph, heading1, heading2 etc. I’m sure this was present in previous versions but seemed to have disappeared in later versions. Sure, I could open up the raw HTML source editor for the content and add the appropriate tags there, but that defeats the object of having a WYSIWYG editor.

After searching for a solution and looking into various plugins which reportedly added this functionality, I came across the simplest solution that was there all along… there is an “HTML block format” check box in the TinyMCE toolbar button configuration that I’d missed!

When this is enabled, a new combo box is added to the TinyMCE toolbar allowing “Paragraph”, “Heading 2”, “Preformatted” etc. formatting styles to be selected.

On Apache SSL and name based virtual hosts

Background

I run several services from my home Linux server to support the home software development I do. This includes services such as Subversion source control, Nexus Maven repository, JIRA issue tracking, Jenkins continuous integration build server to name a few. To provide a clean and consistent entry point into each service, I use separate vhosts in my Apache 2 server, each with its own dedicated sub-domain e.g. nexus.server.com, jira.server.com etc. These vhosts then proxy onto the underlying service using a mixture of HTTP, DAV and AJP13 proxying. [NOTE: I’m using server.com instead of my real domain here simply as an example]

As I want all of these services to be available securely externally I also ensure that they are all served over an SSL connection. Apache is responsible for the SSL handling – SSL is terminated at Apache and then plaintext proxying is used to the underlying service (SVN, Nexus etc.)

The use of SSL necessitates SSL certificates for each vhost, which is pretty straightfoward to implement. I have my own CA certificate which I use to issue certs for each of my services and by adding this CA cert to the truststores on client machines, the certification trust chain can be completed and the SSL connection is verified as valid.

I’ve recently gone through a process of renewing the various SSL certs for my services and moving them to their own dedicated sub-domains (previously they were simply sub-contexts from my main www.server.com domain e.g. www.server.com/nexus, www.server.com/jira etc. rather than having their own sub-domains e.g. nexus.server.com, jira.server.com etc.) Configuring the vhosts in Apache was trivial and on the face of it everything appeared to be working just fine after the changes. Accessing each service from a browser, the SSL certificates were being verified as valid and the web applications were completely functional. It wasn’t until I tried to access my Nexus Maven repository from a Maven command line build that I noticed there was a problem…

Where is that SSL cert coming from?!

On attempting to establish an SSL connection with my Nexus server, the connection was being rejected with a the following exception:

java.security.cert.CertificateException: No name matching nexus.server.com found

I knew that the SSL cert configured in my Nexus vhost was correctly issued for that sub-domain so I couldn’t understand why it was complaining. To investigate further I invoked mvn with the extremely useful Java network debugging property:

-Djavax.net.debug=ssl

This results in the full SSL handshake being logged which can be invaluable when investigating SSL problems. Alternatively, if you’re not using a Java based client and have openssl installed you can use the following command to inspect details of the SSL handshake:

openssl s_client -connect nexus.server.com:443 -status

By doing this I could see that the subject (and specifically the CN element) of the cert being returned by Apache was www.server.com instead of nexus.server.com! Woah – what was going on here…?

The only place that the www.server.com SSL cert was mentioned in my Apache configuration was in the www.server.com vhost – but it couldn’t be using that, could it? As a simple test I tried changing this vhost configuration to use the nexus.server.com cert and then restarted Apache. The next time I ran my mvn command it verified the SSL connection to nexus.server.com with no problems i.e. the SSL server cert now being returned was nexus.server.com! This was very strange I thought… until I started to think more about how Apache name based vhosts actually work…

Limitations of Apache name based virtual hosts

I started Googling on this subject and came across this very interesting Wiki article. This article explains the problem very well, but to summarise… it’s all to do with the way that name based hosts work in Apache: It relies on the Host: header in the HTTP request to determine which vhost this request should be serviced by. In the case of an HTTPS request, this only takes place after the initial SSL connection has been established. Therefore, there isn’t enough information available to Apache in that initial connection establishing phase in order to determine which SSL server cert to return in the SSL handshake. As a result, Apache uses the first SSL cert it finds in the ordered list of vhost configurations. In my case, this was for my www.server.com vhost which explains why it was using the www.server.com SSL cert!

But how could I get round this problem if Apache will only ever use one SSL cert in its SSL handshake?

Introducing wildcard SSL certs

The referenced article mentions the use of wildcard SSL certs i.e. those with a subject name of the form *.server.com. This will only work if all of the subsequent vhosts use genuine server.com sub-domains, but in my case that is true.

To be honest, I’d not really heard of wildcard SSL certs before so after a bit more reading I tried creating one for my domain – with a subject CN of *.server.com. After configuring this in each of my Apache vhosts and a bit of testing everything seems to work just fine. The only difference between now and my previous configuration is that the SSL certs associated with each vhost as less specific than before but I can’t see any problem with that at all.

So, wildcard SSL certs it is from now on!

Debian 6 update broke my subversion repositories!

I recently upgraded my Debian Linux server from version 5 (codename “Etch”) to version 6 (codename “Squeeze”). Considering the size of the upgrade, everything seemed to go very well with only a couple of minor problems during the upgrade. Or so I thought…

The first time I tried to access one of the subversion repositories on my server I encountered the following cryptic error:

svn: OPTIONS of 'https://myserver/svn/myrepo/myproject/trunk': could not connect to server (https://myserver)

I also saw the following error several times too:

svn: Could not open the requested SVN filesystem

After trying an svnlook tree on one of my repos, it reported the following:

# svnlook tree /var/svn/myrepo/
svnlook: Berkeley DB error for filesystem '/var/svn/myrepo/db' while opening environment:
svnlook: DB_VERSION_MISMATCH: Database environment version mismatch
svnlook: bdb: Program version 4.8 doesn't match environment version 4.6

So, it looks like the Debian upgrade has broken my subversion repository access because the version of the Berkeley DB being used has changed! Nice!

After a bit more Googling I found this great blog post which guides you through how to migrate a BDB based svn repository from one version to another. The process basically involves using a couple of BDB utilities for each version of BDB involved (4.6 and 4.8 in my case) to recover the old repository. The blog post also discusses how to migrate a BDB based repository to an FSFS based one which is apparently the recommended repository format these days.

Gigabit LAN update

Following on from previous posts about upgrading my LAN to gigabit capability I’ve just performed some quick speed tests using iperf / JPerf (a Java GUI front-end on iperf).

I tested the speed between 3 points in my network:

  • my HP ML115 G5 Debian Linux server
  • my main Windows XP Pro 32bit PC
  • the family Shuttle Windows Vista Ultimate 32bit PC

Between the Vista PC and Linux server I am seeing a respectable average of 460Mb/s which is roughly 57MB/s.

Between the XP Pro PC and Linux server I am seeing an average of 295Mb/s, roughly 35MB/s.

Between the Vista PC and the XP Pro PC I am seeing almost identical figures to those between the XP Pro PC and the Linux server.

These figures are significantly better than the speeds I saw when it was originally wireless and I’m very happy with the improvement. However, I am not sure why I’m seeing such a difference between the Vista and XP PC speeds… I guess it could be down to a whole host of factors – NIC, OS, driver and configuration etc. I may look at trying a dedicated NIC card in the XP PC but to be honest I think it’ll be fine as it is.

My Linksys E3000 is now running DD-WRT firmware

Well, I left my new Linksys E3000 router running the stock Linksys firmware for a whole day… before deciding to upgrade it to the excellent DD-WRT custom firmware!

I’ve used DD-WRT on my old Linksys WRT-54G routers for a few years now so was very familiar with its features and benefits. I’m still running DD-WRT on a couple of WRT-54Gs acting as client wireless bridges so it made sense to upgrade the E3000 to run the same firmware. The DD-WRT web admin interface is much better than the stock Linksys one, and now all my routers are using the same consistent admin interface it should make administration a little easier.