2009-01-24

Swing Tip: binding an action to a JList

I don't use JList much. It rarely makes it past the experimental version of a UI. A single-column JTable (which is one way of looking at JList) just don't crop up much. JList is easier to use, though, and doesn't look too bad out of the box — in stark contrast to JTable.

(JTable has been enough of a pain in my ass that sometimes I'll even go to the trouble of writing a fancy custom ListCellRenderer to avoid having to use a JTable. But those are stories for other days.)

But when I do use JList, I all too often make the mistake of thinking of it as a component to be double-clicked on, not used from the keyboard. You can see from the lack of any addActionListener method that its designers weren't too sure how to interact with it.

Handling double-click is easy, and it's documented in the class comment for JList. There's basically only one way to do it anyway, so I've never seen anyone get this wrong.

But what about handling enter? That's often neglected, and if you search the web you'll find all kinds of bad advice, from addKeyListener to subclassing JList!

What you really want to do is use the JList's ActionMap and InputMap. If you don't have an Action, just an ActionListener, you'll need to fabricate one, but AbstractAction makes that easy. You can just replace this:

new ActionListener() {
public void actionPerformed(ActionEvent e) {
// whatever...
}
};

With this instead:

new AbstractAction() {
public void actionPerformed(ActionEvent e) {
// whatever...
}
};

You'll need a name, too, because that's how you link the entry in the InputMap with the entry in the ActionMap. Something like:

final String name = "user-hit-enter";
list.getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0), name);
list.getActionMap().put(name, new AbstractAction() {
public void actionPerformed(ActionEvent e) {
// whatever...
}
});

Of course, not being a filthy copy-and-paste merchant, you'll wrap that up in one of your utility classes somewhere. And personally, I'd rather deal with ActionListener in this instance, since the actionPerformed method is all I want from the Action, and an Action is-a ActionListener anyway, so the user can still supply a full-on Action if they happen to have one.

The "clever" part of my tip, though, is to basically have your own JList.addActionListener, and have it take an ActionListener but bind it both to double-click (via addMouseListener) and enter (via getInputMap/getActionMap). That way, you get into the habit of providing both by default.

As usual, there's code for this in my handy library of Java stuff linked to from the side of the page.

2009-01-21

<boost/algorithm/string.hpp>

Item 55 of Scott Meyers' (excellent) "Effective C++" is "Familiarize yourself with Boost". Good advice. The trouble is, those crafty Boost buggers keep adding new stuff.

In 2004, for example, they added <boost/algorithm/string.hpp> to Boost version 1.32. I didn't find out until this past weekend when I needed several of the usual string utilities, and didn't have any of many implementations I've written before (or were written for me by other C++ programmers at the various companies I've worked for).

If you've used C++ and Java, you'll know that C++'s std::basic_string is full of orthogonal but mostly useless methods (find_last_not_of, say) and missing most of the really useful stuff you actually want from your strings (no starts_with, say). The sort of string an academic might design.

Java's java.lang.String, on the other hand, is big bag of random really useful stuff (startsWith, say) whose non-orthogonality can drive you mad (no startsWithIgnoreCase, say). The sort of string people who actually write programs would arrive at.

Boost being what it is, you can now have a big bag of orthogonal useful stuff.

So there's starts_with and ends_with, just like Java, but there's istarts_with and iends_with too.

And you don't just get contains, you get icontains too — something I wanted from Java just yesterday.

There are split functions too, including a regular expression variant. There's even a join in case you change your mind and want to put humpty back together again.

There's a full family of nicely orthogonal trim functions. Not that I personally have ever needed anything more than Java's plain old trim, but it brings a gleam to my shiny icicle of a heart to see all the possible variants laid out before me with sane names, neatly arranged like well-designed German surgical instruments designed as a coherent set by someone with a plan. This in stark contrast to the usual slowly-accreted collection of stains in some god-forsaken header file in a directory called something like "common" or "utils", squirted there by a thousand unthinking deadbeat parents who barely remember their progeny themselves.

Boost's case conversion stuff looks a bit dodgy, so you might end up with upset Turks and Azerbaijanis on your hands if you use it (or, by implication, any of the case-insensitive functions) without checking that out first, but otherwise what I used looked pretty sound. (And if you care about languages other than Seventh Edition New Jersey ASCII, you're probably writing something that should be in Java rather than C++.)

Being Boost, these are all templates too, so they'll work just fine with your home-grown basic_string-that-doesn't-own-the-memory-it-points-to, whatever you call it. (I still haven't found that class in Boost anywhere, which is surprising, because I keep tripping over them everywhere else. Google's variant is called StringPiece, which will have British readers rolling in the aisles. The wide-character variant is sadly not called STGaryGlitter.)

Anyway, I'll endeavor not to write my own C++ starts_with ever again, especially on Linux, where it's trivial to add a build dependency on Boost. And if you read this thinking "I knew that already", why didn't you tell me?

2009-01-19

Accessing GET arguments with libmicrohttpd

The tutorial for libmicrohttpd talks about handling POST arguments, but what if your FORM uses GET? The solution's easy, but I had to work to find it, so this might save someone some trouble in future...

The tutorial contains this example to dump the HTTP headers:

  MHD_get_connection_values(connection, MHD_HEADER_KIND, print_out_key, NULL);

What it doesn't mention is the alternatives to MHD_HEADER_KIND. You don't need to parse the "Referer:" header yourself, as I was about to fall back on; you just need to look at the MHD_GET_ARGUMENT_KIND values. (Or, if it's cookies you're wanting, MHD_COOKIE_KIND.)

Even better, given that you probably know the name of the cookie, GET argument, or header you're looking for in any real code, there's MHD_lookup_connection_value. Here's how you'd get the value of a GET argument called "q", for example:

  const char* q = MHD_lookup_connection_value(connection, MHD_GET_ARGUMENT_KIND, "q");

Don't go putting that straight into a std::string, because libmicrohttpd returns NULL for missing values.

Why am I using libmicrohttpd? Well, that's a story for another day. I'm writing this post because I don't start dealing with the little jobs as soon as they come to me, I'm going to suffer stack overflow.