Monday, September 19, 2011

Wicket Form Submission in IE

No matter how much developers hate it, Internet Explorer is here to stay. And one of its fun little idiosyncrasies is how it handles form submission when a user presses [enter] with a text box selected. In most browsers, doing that would cause the form to be submitted as if it had been submitted by pressing the form's first submit, with the submit's name and value in the request. However, IE does not do that. This problem is universal in web development but the result in Wicket is that the Form's onSubmit() method gets called but none of the Button's onSubmit() methods do. If the Button's onSubmit() method is the one that deals with the data that was submitted, the user isn't going to get the result they expect and it's entirely possible that the information they entered may be lost.

In looking for a way to solve this, I ran across Wicket's setDefaultButton() method. However, that really just changes the rendered HTML in a best-effort attempt to override which button is used by browsers that do submit one by default. But since IE doesn't do that to begin with, setDefaultButton() won't have any effect.

Thankfully, IE's form submission problem can be addressed reliably in Java. The approach I took was to override a Form's delegateSubmit() method. Unless you have default form processing turned off, delegateSubmit() is called after validation passes and the models have been updated, but before any of the onSubmit methods are called. The IFormSubmittingComponent (typically a Button of some sort) that submitted the form is passed in as a parameter and this is the perfect opportunity to set one if there isn't one!

public class DefaultingForm extends Form {
    // Constructors go here...

    protected void delegateSubmit(IFormSubmittingComponent submittingComponent) {
        if(submittingComponent==null) {
            submittingComponent = getDefaultButton();
        }
        super.delegateSubmit(submittingComponent);
    }
}

You could set submittingComponent to any value you want but be warned - since that only happens when no submit is sent by the browser, any conditional logic you put in the if-block may only be seen by IE users. That kind of browser-specific behavior could be considered just as bad as the behavior we are trying to remedy! Instead, using the value of getDefaultButton() helps ensure that all browsers will get the same result. Browsers that choose a default button will benefit from the markup changes setDefaultButton() causes. And IE will benefit by having the same button chosen for it. I'm not sure why an approach like this wouldn't be used by Wicket itself. Thoughts?

Thursday, September 8, 2011

Configuring an OpenCV 2 project in Eclipse

I recently picked up a book on OpenCV 2 but all the setup instructions are mostly geared toward developing in either Visual Studio or Qt. Since I mostly do Java development, I would prefer to use Eclipse and I happen to use a Mac at home. And I haven't done much C++ development in Eclipse so I wasn't sure how to get things setup. Setup is actually pretty easy once you know the right values. I've seen some incorrect (maybe just outdated?) info about this on the internet so hopefully this updated info will be useful for people like myself who are new to OpenCV, as well as the C++/Eclipse/Mac combo.

Once you have CDT installed, download the OpenCV source from their site, opencv.willowgarage.com. OpenCV uses a tool called CMake to generate make scripts for the build tool of your choice. Since I'm not very familiar with Xcode, I followed the Linux instructions, where you generate files for make. Using the default build configuration in CMake has worked well for me so far. In a Terminal, navigate to the directory where the Makefile was generated and run "make". Then run "sudo make install" to copy the headers and dylibs where they belong, in /usr/local/include/ and /usr/local/lib/ respectively.

Once those are built and installed, you can creata a C++ project in Eclipse. Then, the trick to getting an OpenCV project to compile and run is to go to the Project Properties and select Settings, under C/C++ Build. On the Tool Settings tab, under MacOS X C++ Linker, select Libraries. Add /usr/local/lib as a library search path. Then add the following as libraries:

  • opencv_calib3d
  • opencv_contrib
  • opencv_core
  • opencv_features2d
  • opencv_flann
  • opencv_gpu
  • opencv_highgui
  • opencv_imgproc
  • opencv_legacy
  • opencv_ml
  • opencv_objdetect
  • opencv_ts
  • opencv_video

Those values correspond to files in the library search path but with the "lib" prefix and ".dylib" extension stripped off. So if your build relies on libfoo.dylib, you would simply add "foo" as a library.

You may also notice that the OpenCV build process creates quite a few files in the library directory. Most of those are aliases/links that allow you to be as specific (or not) as you want about which version to use. A library like opencv_core should point to the most recent version. But the most recent version tomorrow might have a totally different API than it did yesterday, so watch out. The opencv_core.2.3 library points to the most recent 2.3.x release. And for me, as of right now, opencv_core.2.3.1 is that most recent version.